From ea358eef36f67fd4408b0990e07d65df514c33b5 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Fri, 27 Mar 2026 13:26:11 +0100 Subject: [PATCH 1/5] chore: package audit and remove dead code --- package-lock.json | 1751 ++++++++++++++++++++++++++++------------- package.json | 23 +- src/auth/s3-client.ts | 122 --- 3 files changed, 1215 insertions(+), 681 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24ef051..86b65a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,31 +10,30 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@aws-sdk/client-s3": "^3.1000.0", - "@aws-sdk/credential-providers": "^3.1000.0", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@tigrisdata/iam": "^1.3.0", - "@tigrisdata/storage": "^2.15.5", + "@aws-sdk/credential-providers": "^3.1018.0", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@tigrisdata/iam": "^1.4.1", + "@tigrisdata/storage": "^2.15.6", "axios": "^1.13.6", "commander": "^14.0.3", "enquirer": "^2.4.1", - "jose": "^6.1.3", + "jose": "^6.2.2", "open": "^11.0.0", - "yaml": "^2.8.2" + "yaml": "^2.8.3" }, "bin": { "t3": "dist/cli.js", "tigris": "dist/cli.js" }, "devDependencies": { - "@commitlint/cli": "^20.4.2", - "@commitlint/config-conventional": "^20.4.2", + "@commitlint/cli": "^20.5.0", + "@commitlint/config-conventional": "^20.5.0", "@eslint/js": "^10.0.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@types/node": "^22.19.11", "dotenv": "^17.3.1", - "eslint": "^10.0.2", + "eslint": "^10.1.0", "husky": "^9.1.7", "prettier": "^3.8.1", "publint": "^0.3.18", @@ -42,8 +41,8 @@ "tsup": "^8.5.1", "tsx": "^4.21.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.56.1", - "vitest": "^4.0.18" + "typescript-eslint": "^8.57.2", + "vitest": "^4.1.2" } }, "node_modules/@actions/core": { @@ -298,45 +297,45 @@ } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.1011.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1011.0.tgz", - "integrity": "sha512-+B5jc1KuRLO7hNVCbA+BySNNe8+aclgVY+K+uwhrvbumMxK4RFi4VWMPliPq2u7HA4BwP/FhqjNu6zNjR/Y3Zg==", + "version": "3.1018.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1018.0.tgz", + "integrity": "sha512-BRF3W1H8Ews1pomU6gJ/LtKvXezJofABrj6L928Uex89QafRiDik2c1bZ15woci2XdhtfHo4p0nxYOQcXsbUlw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/credential-provider-node": "^3.972.26", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", - "@aws-sdk/middleware-recursion-detection": "^3.972.8", - "@aws-sdk/middleware-user-agent": "^3.972.21", - "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.26", + "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", - "@aws-sdk/util-user-agent-node": "^3.973.7", - "@smithy/config-resolver": "^4.4.11", - "@smithy/core": "^3.23.11", + "@aws-sdk/util-user-agent-node": "^3.973.12", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", - "@smithy/middleware-endpoint": "^4.4.25", - "@smithy/middleware-retry": "^4.4.42", - "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", - "@smithy/node-http-handler": "^4.4.16", + "@smithy/node-http-handler": "^4.5.0", "@smithy/protocol-http": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.41", - "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", @@ -414,19 +413,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.20.tgz", - "integrity": "sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA==", + "version": "3.973.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.25.tgz", + "integrity": "sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", - "@aws-sdk/xml-builder": "^3.972.11", - "@smithy/core": "^3.23.11", + "@aws-sdk/xml-builder": "^3.972.16", + "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", @@ -451,12 +450,12 @@ } }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.13.tgz", - "integrity": "sha512-WZnIK8NPX+4OXkpVoNmUS+Ya1osqjszUsDqFEz97+a/LD5K012np9iR/eWEC43btx8zQjyRIK8kyiwbh8SiHzg==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.18.tgz", + "integrity": "sha512-1Amo/hA/mzR6BR67Ts4Hnr7Z2WVPuyqv+N58HiYvR9SovfRP+BiqHRujn0tM7/4cJa9687yvAdcYaEFeJQc2tQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", @@ -467,12 +466,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.18.tgz", - "integrity": "sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==", + "version": "3.972.23", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.23.tgz", + "integrity": "sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", @@ -483,20 +482,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.20.tgz", - "integrity": "sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.25.tgz", + "integrity": "sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/fetch-http-handler": "^5.3.15", - "@smithy/node-http-handler": "^4.4.16", + "@smithy/node-http-handler": "^4.5.0", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" }, "engines": { @@ -504,19 +503,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.20.tgz", - "integrity": "sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/credential-provider-env": "^3.972.18", - "@aws-sdk/credential-provider-http": "^3.972.20", - "@aws-sdk/credential-provider-login": "^3.972.20", - "@aws-sdk/credential-provider-process": "^3.972.18", - "@aws-sdk/credential-provider-sso": "^3.972.20", - "@aws-sdk/credential-provider-web-identity": "^3.972.20", - "@aws-sdk/nested-clients": "^3.996.10", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.25.tgz", + "integrity": "sha512-G/v/PicYn4qs7xCv4vT6I4QKdvMyRvsgIFNBkUueCGlbLo7/PuKcNKgUozmLSsaYnE7jIl6UrfkP07EUubr48w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/credential-provider-env": "^3.972.23", + "@aws-sdk/credential-provider-http": "^3.972.25", + "@aws-sdk/credential-provider-login": "^3.972.25", + "@aws-sdk/credential-provider-process": "^3.972.23", + "@aws-sdk/credential-provider-sso": "^3.972.25", + "@aws-sdk/credential-provider-web-identity": "^3.972.25", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", @@ -529,13 +528,13 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.20.tgz", - "integrity": "sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.25.tgz", + "integrity": "sha512-bUdmyJeVua7SmD+g2a65x2/0YqsGn4K2k4GawI43js0odaNaIzpIhLtHehUnPnfLuyhPWbJR1NyuIO4iMVfM0w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", @@ -548,17 +547,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.21.tgz", - "integrity": "sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==", + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.26.tgz", + "integrity": "sha512-5XSK74rCXxCNj+UWv5bjq1EccYkiyW4XOHFU9NXnsCcQF8dJuHdua1qFg0m/LIwVOWklbKsrcnMtfxIXwgvwzQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.18", - "@aws-sdk/credential-provider-http": "^3.972.20", - "@aws-sdk/credential-provider-ini": "^3.972.20", - "@aws-sdk/credential-provider-process": "^3.972.18", - "@aws-sdk/credential-provider-sso": "^3.972.20", - "@aws-sdk/credential-provider-web-identity": "^3.972.20", + "@aws-sdk/credential-provider-env": "^3.972.23", + "@aws-sdk/credential-provider-http": "^3.972.25", + "@aws-sdk/credential-provider-ini": "^3.972.25", + "@aws-sdk/credential-provider-process": "^3.972.23", + "@aws-sdk/credential-provider-sso": "^3.972.25", + "@aws-sdk/credential-provider-web-identity": "^3.972.25", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", @@ -571,12 +570,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.18", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.18.tgz", - "integrity": "sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==", + "version": "3.972.23", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.23.tgz", + "integrity": "sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", @@ -588,14 +587,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.20.tgz", - "integrity": "sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.25.tgz", + "integrity": "sha512-r4OGAfHmlEa1QBInHWz+/dOD4tRljcjVNQe9wJ/AJNXEj1d2WdsRLppvRFImRV6FIs+bTpjtL0a23V5ELQpRPw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/nested-clients": "^3.996.10", - "@aws-sdk/token-providers": "3.1009.0", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/nested-clients": "^3.996.15", + "@aws-sdk/token-providers": "3.1018.0", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", @@ -607,13 +606,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.20.tgz", - "integrity": "sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.25.tgz", + "integrity": "sha512-uM1OtoJgj+yK3MlAmda8uR9WJJCdm5HB25JyCeFL5a5q1Fbafalf4uKidFO3/L0Pgd+Fsflkb4cM6jHIswi3QQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", @@ -625,26 +624,26 @@ } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.1011.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1011.0.tgz", - "integrity": "sha512-Ev3ZOqB+vBdg5WoKbyg/FQqXoICM+H5OoL7qPqbiww3LGIVuKbxbTBUtxhOynnyiKt1yD6rIP6KxL3XjXxTAXg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.1011.0", - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/credential-provider-cognito-identity": "^3.972.13", - "@aws-sdk/credential-provider-env": "^3.972.18", - "@aws-sdk/credential-provider-http": "^3.972.20", - "@aws-sdk/credential-provider-ini": "^3.972.20", - "@aws-sdk/credential-provider-login": "^3.972.20", - "@aws-sdk/credential-provider-node": "^3.972.21", - "@aws-sdk/credential-provider-process": "^3.972.18", - "@aws-sdk/credential-provider-sso": "^3.972.20", - "@aws-sdk/credential-provider-web-identity": "^3.972.20", - "@aws-sdk/nested-clients": "^3.996.10", + "version": "3.1018.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1018.0.tgz", + "integrity": "sha512-Lou9mLRBRDEknOxJ0KBHcIZ5H0BCzlpsHXrSDqkil8kOxPBqxa56s3dS6S0Y/aVl2u7Nd1oOk4IHUf3A+WdqMQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.1018.0", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/credential-provider-cognito-identity": "^3.972.18", + "@aws-sdk/credential-provider-env": "^3.972.23", + "@aws-sdk/credential-provider-http": "^3.972.25", + "@aws-sdk/credential-provider-ini": "^3.972.25", + "@aws-sdk/credential-provider-login": "^3.972.25", + "@aws-sdk/credential-provider-node": "^3.972.26", + "@aws-sdk/credential-provider-process": "^3.972.23", + "@aws-sdk/credential-provider-sso": "^3.972.25", + "@aws-sdk/credential-provider-web-identity": "^3.972.25", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", - "@smithy/config-resolver": "^4.4.11", - "@smithy/core": "^3.23.11", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", @@ -778,9 +777,9 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", - "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.9.tgz", + "integrity": "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", @@ -833,15 +832,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.21.tgz", - "integrity": "sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==", + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.26.tgz", + "integrity": "sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", - "@smithy/core": "^3.23.11", + "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.12", @@ -852,44 +851,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.996.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.10.tgz", - "integrity": "sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==", + "version": "3.996.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.15.tgz", + "integrity": "sha512-k6WAVNkub5DrU46iPQvH1m0xc1n+0dX79+i287tYJzf5g1yU2rX3uf4xNeL5JvK1NtYgfwMnsxHqhOXFBn367A==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.25", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", - "@aws-sdk/middleware-recursion-detection": "^3.972.8", - "@aws-sdk/middleware-user-agent": "^3.972.21", - "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.26", + "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", - "@aws-sdk/util-user-agent-node": "^3.973.7", - "@smithy/config-resolver": "^4.4.11", - "@smithy/core": "^3.23.11", + "@aws-sdk/util-user-agent-node": "^3.973.12", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", - "@smithy/middleware-endpoint": "^4.4.25", - "@smithy/middleware-retry": "^4.4.42", - "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", - "@smithy/node-http-handler": "^4.4.16", + "@smithy/node-http-handler": "^4.5.0", "@smithy/protocol-http": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.41", - "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", @@ -901,13 +900,13 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.8.tgz", - "integrity": "sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.10.tgz", + "integrity": "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", - "@smithy/config-resolver": "^4.4.11", + "@smithy/config-resolver": "^4.4.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" @@ -953,13 +952,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1009.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1009.0.tgz", - "integrity": "sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==", + "version": "3.1018.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1018.0.tgz", + "integrity": "sha512-97OPNJHy37wmGOX44xAcu6E9oSTiqK9uPcy/fWpmN5uB3JuEp1f6x60Xot/jp+FxwhQWIFUsVJFnm3QKqt7T6Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", - "@aws-sdk/nested-clients": "^3.996.10", + "@aws-sdk/core": "^3.973.25", + "@aws-sdk/nested-clients": "^3.996.15", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", @@ -1051,12 +1050,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.7.tgz", - "integrity": "sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==", + "version": "3.973.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.12.tgz", + "integrity": "sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/middleware-user-agent": "^3.972.26", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", @@ -1076,13 +1075,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.11.tgz", - "integrity": "sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.16.tgz", + "integrity": "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.13.1", - "fast-xml-parser": "5.4.1", + "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" }, "engines": { @@ -1135,17 +1134,17 @@ } }, "node_modules/@commitlint/cli": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.4.2.tgz", - "integrity": "sha512-YjYSX2yj/WsVoxh9mNiymfFS2ADbg2EK4+1WAsMuckwKMCqJ5PDG0CJU/8GvmHWcv4VRB2V02KqSiecRksWqZQ==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.0.tgz", + "integrity": "sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/format": "^20.4.0", - "@commitlint/lint": "^20.4.2", - "@commitlint/load": "^20.4.0", - "@commitlint/read": "^20.4.0", - "@commitlint/types": "^20.4.0", + "@commitlint/format": "^20.5.0", + "@commitlint/lint": "^20.5.0", + "@commitlint/load": "^20.5.0", + "@commitlint/read": "^20.5.0", + "@commitlint/types": "^20.5.0", "tinyexec": "^1.0.0", "yargs": "^17.0.0" }, @@ -1157,27 +1156,27 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.4.2.tgz", - "integrity": "sha512-rwkTF55q7Q+6dpSKUmJoScV0f3EpDlWKw2UPzklkLS4o5krMN1tPWAVOgHRtyUTMneIapLeQwaCjn44Td6OzBQ==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.0.tgz", + "integrity": "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", - "conventional-changelog-conventionalcommits": "^9.1.0" + "@commitlint/types": "^20.5.0", + "conventional-changelog-conventionalcommits": "^9.2.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/config-validator": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.4.0.tgz", - "integrity": "sha512-zShmKTF+sqyNOfAE0vKcqnpvVpG0YX8F9G/ZIQHI2CoKyK+PSdladXMSns400aZ5/QZs+0fN75B//3Q5CHw++w==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", + "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "ajv": "^8.11.0" }, "engines": { @@ -1185,13 +1184,13 @@ } }, "node_modules/@commitlint/ensure": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.4.1.tgz", - "integrity": "sha512-WLQqaFx1pBooiVvBrA1YfJNFqZF8wS/YGOtr5RzApDbV9tQ52qT5VkTsY65hFTnXhW8PcDfZLaknfJTmPejmlw==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz", + "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -1213,13 +1212,13 @@ } }, "node_modules/@commitlint/format": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.4.0.tgz", - "integrity": "sha512-i3ki3WR0rgolFVX6r64poBHXM1t8qlFel1G1eCBvVgntE3fCJitmzSvH5JD/KVJN/snz6TfaX2CLdON7+s4WVQ==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", + "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "picocolors": "^1.1.1" }, "engines": { @@ -1227,13 +1226,13 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.4.1.tgz", - "integrity": "sha512-In5EO4JR1lNsAv1oOBBO24V9ND1IqdAJDKZiEpdfjDl2HMasAcT7oA+5BKONv1pRoLG380DGPE2W2RIcUwdgLA==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", + "@commitlint/types": "^20.5.0", "semver": "^7.6.0" }, "engines": { @@ -1241,33 +1240,33 @@ } }, "node_modules/@commitlint/lint": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.4.2.tgz", - "integrity": "sha512-buquzNRtFng6xjXvBU1abY/WPEEjCgUipNQrNmIWe8QuJ6LWLtei/LDBAzEe5ASm45+Q9L2Xi3/GVvlj50GAug==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz", + "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^20.4.1", - "@commitlint/parse": "^20.4.1", - "@commitlint/rules": "^20.4.2", - "@commitlint/types": "^20.4.0" + "@commitlint/is-ignored": "^20.5.0", + "@commitlint/parse": "^20.5.0", + "@commitlint/rules": "^20.5.0", + "@commitlint/types": "^20.5.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.4.0.tgz", - "integrity": "sha512-Dauup/GfjwffBXRJUdlX/YRKfSVXsXZLnINXKz0VZkXdKDcaEILAi9oflHGbfydonJnJAbXEbF3nXPm9rm3G6A==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.0.tgz", + "integrity": "sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.4.0", + "@commitlint/config-validator": "^20.5.0", "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.4.0", - "@commitlint/types": "^20.4.0", - "cosmiconfig": "^9.0.0", + "@commitlint/resolve-extends": "^20.5.0", + "@commitlint/types": "^20.5.0", + "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "is-plain-obj": "^4.1.0", "lodash.mergewith": "^4.6.2", @@ -1278,9 +1277,9 @@ } }, "node_modules/@commitlint/message": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.0.tgz", - "integrity": "sha512-B5lGtvHgiLAIsK5nLINzVW0bN5hXv+EW35sKhYHE8F7V9Uz1fR4tx3wt7mobA5UNhZKUNgB/+ldVMQE6IHZRyA==", + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", + "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", "dev": true, "license": "MIT", "engines": { @@ -1288,30 +1287,30 @@ } }, "node_modules/@commitlint/parse": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.4.1.tgz", - "integrity": "sha512-XNtZjeRcFuAfUnhYrCY02+mpxwY4OmnvD3ETbVPs25xJFFz1nRo/25nHj+5eM+zTeRFvWFwD4GXWU2JEtoK1/w==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", + "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.4.0", - "conventional-changelog-angular": "^8.1.0", - "conventional-commits-parser": "^6.2.1" + "@commitlint/types": "^20.5.0", + "conventional-changelog-angular": "^8.2.0", + "conventional-commits-parser": "^6.3.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/read": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.4.0.tgz", - "integrity": "sha512-QfpFn6/I240ySEGv7YWqho4vxqtPpx40FS7kZZDjUJ+eHxu3azfhy7fFb5XzfTqVNp1hNoI3tEmiEPbDB44+cg==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", + "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/top-level": "^20.4.0", - "@commitlint/types": "^20.4.0", - "git-raw-commits": "^4.0.0", + "@commitlint/top-level": "^20.4.3", + "@commitlint/types": "^20.5.0", + "git-raw-commits": "^5.0.0", "minimist": "^1.2.8", "tinyexec": "^1.0.0" }, @@ -1320,14 +1319,14 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.4.0.tgz", - "integrity": "sha512-ay1KM8q0t+/OnlpqXJ+7gEFQNlUtSU5Gxr8GEwnVf2TPN3+ywc5DzL3JCxmpucqxfHBTFwfRMXxPRRnR5Ki20g==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.0.tgz", + "integrity": "sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.4.0", - "@commitlint/types": "^20.4.0", + "@commitlint/config-validator": "^20.5.0", + "@commitlint/types": "^20.5.0", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", @@ -1338,16 +1337,16 @@ } }, "node_modules/@commitlint/rules": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.4.2.tgz", - "integrity": "sha512-oz83pnp5Yq6uwwTAabuVQPNlPfeD2Y5ZjMb7Wx8FSUlu4sLYJjbBWt8031Z0osCFPfHzAwSYrjnfDFKtuSMdKg==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz", + "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/ensure": "^20.4.1", - "@commitlint/message": "^20.4.0", + "@commitlint/ensure": "^20.5.0", + "@commitlint/message": "^20.4.3", "@commitlint/to-lines": "^20.0.0", - "@commitlint/types": "^20.4.0" + "@commitlint/types": "^20.5.0" }, "engines": { "node": ">=v18" @@ -1364,9 +1363,9 @@ } }, "node_modules/@commitlint/top-level": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.0.tgz", - "integrity": "sha512-NDzq8Q6jmFaIIBC/GG6n1OQEaHdmaAAYdrZRlMgW6glYWGZ+IeuXmiymDvQNXPc82mVxq2KiE3RVpcs+1OeDeA==", + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", + "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1377,19 +1376,80 @@ } }, "node_modules/@commitlint/types": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.4.0.tgz", - "integrity": "sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==", + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", + "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", "dev": true, "license": "MIT", "dependencies": { - "conventional-commits-parser": "^6.2.1", + "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=v18" } }, + "node_modules/@conventional-changelog/git-client": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.6.0.tgz", + "integrity": "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^1.0.0", + "@simple-libs/stream-utils": "^1.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.3.0" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", @@ -1862,37 +1922,37 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz", - "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==", + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.2", + "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", - "minimatch": "^10.2.1" + "minimatch": "^10.2.4" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", - "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0" + "@eslint/core": "^1.1.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", - "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1924,9 +1984,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", - "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1934,13 +1994,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", - "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0", + "@eslint/core": "^1.1.1", "levn": "^0.4.1" }, "engines": { @@ -2038,6 +2098,23 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", @@ -2194,6 +2271,16 @@ "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -2252,6 +2339,268 @@ "url": "https://bjornlu.com/sponsor" } }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", @@ -3175,6 +3524,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@simple-libs/child-process-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", + "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/stream-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", + "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -3240,9 +3618,9 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", - "integrity": "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", + "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.12", @@ -3477,9 +3855,9 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.26", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.26.tgz", - "integrity": "sha512-8Qfikvd2GVKSm8S6IbjfwFlRY9VlMrj0Dp4vTwAuhqbX7NhJKE5DQc2bnfJIcY0B+2YKMDBWfvexbSZeejDgeg==", + "version": "4.4.27", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.27.tgz", + "integrity": "sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.23.12", @@ -3496,15 +3874,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.43", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.43.tgz", - "integrity": "sha512-ZwsifBdyuNHrFGmbc7bAfP2b54+kt9J2rhFd18ilQGAB+GDiP4SrawqyExbB7v455QVR7Psyhb2kjULvBPIhvA==", + "version": "4.4.44", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.44.tgz", + "integrity": "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", - "@smithy/smithy-client": "^4.12.6", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", @@ -3672,13 +4050,13 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.6.tgz", - "integrity": "sha512-aib3f0jiMsJ6+cvDnXipBsGDL7ztknYSVqJs1FdN9P+u9tr/VzOR7iygSh6EUOdaBeMCMSh3N0VdyYsG4o91DQ==", + "version": "4.12.7", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.7.tgz", + "integrity": "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.23.12", - "@smithy/middleware-endpoint": "^4.4.26", + "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", @@ -3779,13 +4157,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.42", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.42.tgz", - "integrity": "sha512-0vjwmcvkWAUtikXnWIUOyV6IFHTEeQUYh3JUZcDgcszF+hD/StAsQ3rCZNZEPHgI9kVNcbnyc8P2CBHnwgmcwg==", + "version": "4.3.43", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.43.tgz", + "integrity": "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.6", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -3794,16 +4172,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.45", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.45.tgz", - "integrity": "sha512-q5dOqqfTgUcLe38TAGiFn9srToKj2YCHJ34QGOLzM+xYLLA+qRZv7N+33kl1MERVusue36ZHnlNaNEvY/PzSrw==", + "version": "4.2.47", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.47.tgz", + "integrity": "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.11", + "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.6", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -3942,18 +4320,18 @@ "license": "MIT" }, "node_modules/@tigrisdata/iam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@tigrisdata/iam/-/iam-1.3.0.tgz", - "integrity": "sha512-t8ZH1ZjzE11TCRSUk24PvayGhpoAGCdihGG2upZG6nC9P4B6fHsVaqUzNo3IXkr4uBi4/rkovJ5eXMgF7rUxqA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@tigrisdata/iam/-/iam-1.4.1.tgz", + "integrity": "sha512-AQbFKbdosGuQoyKE0Mi90oUqbtsQ1sN1u67oABHod9hCqa3y558wxVZ9t/hkGdiDBwA1IkxAC6vvKTeJkrGDMA==", "license": "MIT", "dependencies": { - "dotenv": "^17.2.1" + "dotenv": "^17.3.1" } }, "node_modules/@tigrisdata/storage": { - "version": "2.15.5", - "resolved": "https://registry.npmjs.org/@tigrisdata/storage/-/storage-2.15.5.tgz", - "integrity": "sha512-U/59S5JR8y5CvbFDV6OKlxOyafKbV6g4tga3PjGoh6Ugl0eN7GIjilDCcuL1DwI60VX7CF3lyuC7egcLnqUHNA==", + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/@tigrisdata/storage/-/storage-2.15.6.tgz", + "integrity": "sha512-DLMdiw6BLCqJ5M9gKg33MHRCYQdKhAxvpF2OSTj61/rtGOo5ZUswjOW80osLJGM4KGZIbisWQYkQ0shF52Z4AA==", "license": "MIT", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", @@ -3964,6 +4342,17 @@ "dotenv": "^17.3.1" } }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -4021,17 +4410,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", - "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/type-utils": "8.56.1", - "@typescript-eslint/utils": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -4044,22 +4433,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.56.1", + "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", - "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "engines": { @@ -4075,14 +4464,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", - "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.1", - "@typescript-eslint/types": "^8.56.1", + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "engines": { @@ -4097,14 +4486,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", - "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4115,9 +4504,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", - "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", "engines": { @@ -4132,15 +4521,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", - "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -4157,9 +4546,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", - "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, "license": "MIT", "engines": { @@ -4171,16 +4560,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", - "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.56.1", - "@typescript-eslint/tsconfig-utils": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -4199,16 +4588,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", - "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1" + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4223,13 +4612,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", - "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4254,31 +4643,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", + "@vitest/spy": "4.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -4287,7 +4676,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -4299,26 +4688,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.2", "pathe": "^2.0.3" }, "funding": { @@ -4326,13 +4715,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -4341,9 +4731,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", "dev": true, "license": "MIT", "funding": { @@ -4351,14 +4741,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "tinyrainbow": "^3.0.3" + "@vitest/pretty-format": "4.1.2", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -4588,9 +4979,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4932,9 +5323,9 @@ } }, "node_modules/conventional-changelog-angular": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.1.0.tgz", - "integrity": "sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz", + "integrity": "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==", "dev": true, "license": "ISC", "dependencies": { @@ -4945,9 +5336,9 @@ } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.1.0.tgz", - "integrity": "sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.0.tgz", + "integrity": "sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==", "dev": true, "license": "ISC", "dependencies": { @@ -4987,12 +5378,13 @@ } }, "node_modules/conventional-commits-parser": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.2.1.tgz", - "integrity": "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz", + "integrity": "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==", "dev": true, "license": "MIT", "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { @@ -5015,6 +5407,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -5023,9 +5422,9 @@ "license": "MIT" }, "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5103,22 +5502,9 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dargs": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", - "dev": true, - "license": "MIT", + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5208,6 +5594,16 @@ "node": ">=0.4.0" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5507,9 +5903,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -5606,18 +6002,18 @@ } }, "node_modules/eslint": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz", - "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.1.0.tgz", + "integrity": "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.2", - "@eslint/config-helpers": "^0.5.2", - "@eslint/core": "^1.1.0", - "@eslint/plugin-kit": "^0.6.0", + "@eslint/config-array": "^0.23.3", + "@eslint/config-helpers": "^0.5.3", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5626,9 +6022,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.1", + "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", - "espree": "^11.1.1", + "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5639,7 +6035,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.1", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -5662,9 +6058,9 @@ } }, "node_modules/eslint-scope": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", - "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5741,9 +6137,9 @@ "license": "MIT" }, "node_modules/espree": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz", - "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5941,9 +6337,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", - "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", "funding": [ { "type": "github", @@ -5952,8 +6348,9 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.0.0", - "strnum": "^2.1.2" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -6292,34 +6689,20 @@ } }, "node_modules/git-raw-commits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", + "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", "dev": true, "license": "MIT", "dependencies": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" + "@conventional-changelog/git-client": "^2.6.0", + "meow": "^13.0.0" }, "bin": { - "git-raw-commits": "cli.mjs" + "git-raw-commits": "src/cli.js" }, "engines": { - "node": ">=16" - } - }, - "node_modules/git-raw-commits/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, "node_modules/glob-parent": { @@ -6371,9 +6754,9 @@ "license": "ISC" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6890,9 +7273,9 @@ } }, "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -7000,6 +7383,267 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -9805,9 +10449,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", + "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", "funding": [ { "type": "github", @@ -9854,9 +10498,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -9976,9 +10620,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, "funding": [ { @@ -10370,6 +11014,40 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -11067,16 +11745,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -11085,9 +11753,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, @@ -11192,9 +11860,9 @@ } }, "node_modules/strnum": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", - "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", "funding": [ { "type": "github", @@ -11455,9 +12123,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -11468,9 +12136,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -11514,9 +12182,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -11667,16 +12335,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", - "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.56.1", - "@typescript-eslint/parser": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1" + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11822,17 +12490,16 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "bin": { @@ -11849,9 +12516,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -11864,13 +12532,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { "optional": true }, - "lightningcss": { + "jiti": { + "optional": true + }, + "less": { "optional": true }, "sass": { @@ -11896,28 +12567,10 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -11928,31 +12581,31 @@ } }, "node_modules/vitest": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", - "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.18", - "@vitest/mocker": "4.0.18", - "@vitest/pretty-format": "4.0.18", - "@vitest/runner": "4.0.18", - "@vitest/snapshot": "4.0.18", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -11968,12 +12621,13 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.18", - "@vitest/browser-preview": "4.0.18", - "@vitest/browser-webdriverio": "4.0.18", - "@vitest/ui": "4.0.18", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -12002,13 +12656,16 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -12130,9 +12787,9 @@ } }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 31860c8..1e9463b 100644 --- a/package.json +++ b/package.json @@ -80,27 +80,26 @@ ] }, "dependencies": { - "@aws-sdk/client-s3": "^3.1000.0", - "@aws-sdk/credential-providers": "^3.1000.0", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@tigrisdata/iam": "^1.3.0", - "@tigrisdata/storage": "^2.15.5", + "@aws-sdk/credential-providers": "^3.1018.0", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@tigrisdata/iam": "^1.4.1", + "@tigrisdata/storage": "^2.15.6", "axios": "^1.13.6", "commander": "^14.0.3", "enquirer": "^2.4.1", - "jose": "^6.1.3", + "jose": "^6.2.2", "open": "^11.0.0", - "yaml": "^2.8.2" + "yaml": "^2.8.3" }, "devDependencies": { - "@commitlint/cli": "^20.4.2", - "@commitlint/config-conventional": "^20.4.2", + "@commitlint/cli": "^20.5.0", + "@commitlint/config-conventional": "^20.5.0", "@eslint/js": "^10.0.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@types/node": "^22.19.11", "dotenv": "^17.3.1", - "eslint": "^10.0.2", + "eslint": "^10.1.0", "husky": "^9.1.7", "prettier": "^3.8.1", "publint": "^0.3.18", @@ -108,7 +107,7 @@ "tsup": "^8.5.1", "tsx": "^4.21.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.56.1", - "vitest": "^4.0.18" + "typescript-eslint": "^8.57.2", + "vitest": "^4.1.2" } } diff --git a/src/auth/s3-client.ts b/src/auth/s3-client.ts index a841ddd..8bda217 100644 --- a/src/auth/s3-client.ts +++ b/src/auth/s3-client.ts @@ -3,8 +3,6 @@ * Creates appropriate S3 client based on login method */ -import { S3Client } from '@aws-sdk/client-s3'; -import type { HttpRequest } from '@aws-sdk/types'; import { fromIni } from '@aws-sdk/credential-providers'; import { getLoginMethod as getStoredLoginMethod, @@ -157,126 +155,6 @@ export async function getStorageConfig(options?: { ); } -/** - * Get configured S3 client based on login method - */ -export async function getS3Client(): Promise { - // 1. AWS profile (only if AWS_PROFILE is set) - if (hasAwsProfile()) { - const profile = process.env.AWS_PROFILE || 'default'; - const profileConfig = await getAwsProfileConfig(profile); - const client = new S3Client({ - region: 'auto', - endpoint: - profileConfig.endpoint || - tigrisConfig.endpoint || - DEFAULT_STORAGE_ENDPOINT, - credentials: fromIni({ profile }), - }); - - return client; - } - - // 2. Login (oauth or credentials) - const loginMethod = await getLoginMethod(); - - if (loginMethod === 'oauth') { - const authClient = getAuthClient(); - const selectedOrg = getSelectedOrganization(); - - if (!selectedOrg) { - throw new Error( - 'No organization selected. Please run "tigris orgs select" first.' - ); - } - - const credentialProvider = async () => ({ - accessKeyId: '', - secretAccessKey: '', - sessionToken: await authClient.getAccessToken(), - expiration: new Date(Date.now() + 10 * 60 * 1000), - }); - - const client = new S3Client({ - region: 'auto', - endpoint: tigrisConfig.endpoint, - credentials: credentialProvider, - }); - - // Add middleware to inject custom headers - client.middlewareStack.add( - (next) => async (args) => { - const req = args.request as HttpRequest; - req.headers['x-Tigris-Namespace'] = selectedOrg; - const result = await next(args); - return result; - }, - { - name: 'x-Tigris-Namespace-Middleware', - step: 'build', - override: true, - } - ); - - return client; - } - - if (loginMethod === 'credentials') { - const loginCredentials = getStoredCredentials(); - if (loginCredentials) { - const client = new S3Client({ - region: 'auto', - endpoint: loginCredentials.endpoint, - credentials: { - accessKeyId: loginCredentials.accessKeyId, - secretAccessKey: loginCredentials.secretAccessKey, - }, - }); - - return client; - } - } - - // 3. Env vars - const envCredentials = getEnvCredentials(); - if (envCredentials) { - const client = new S3Client({ - region: 'auto', - endpoint: envCredentials.endpoint, - credentials: { - accessKeyId: envCredentials.accessKeyId, - secretAccessKey: envCredentials.secretAccessKey, - }, - }); - - return client; - } - - // 4. Configured credentials - const credentials = getStoredCredentials(); - - if (credentials) { - const client = new S3Client({ - region: 'auto', - endpoint: credentials.endpoint, - credentials: { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - }, - }); - - return client; - } - - // No valid auth method found — try auto-login in interactive terminals - if (await triggerAutoLogin()) { - return getS3Client(); - } - throw new Error( - 'Not authenticated. Please run "tigris login" or "tigris configure" first.' - ); -} - /** * Check if user is authenticated (either method) */ From dceebd160e3af102e321bb1717f03c00eabc412d Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Sun, 29 Mar 2026 10:30:31 +0200 Subject: [PATCH 2/5] refactor: fix imports Renames modules, fold configs where they belong. Uses tsconfig paths for cleaner imports --- eslint.config.js | 8 + package-lock.json | 11 + package.json | 3 +- src/auth/client.ts | 72 ++++- src/auth/config.ts | 68 ---- src/auth/fly.ts | 5 +- src/auth/{s3-client.ts => provider.ts} | 53 +++- src/auth/storage.ts | 308 +++++++++++++----- src/auth/types.ts | 34 -- src/cli-binary.ts | 12 +- src/cli-core.ts | 5 +- src/cli.ts | 11 +- src/lib/access-keys/assign.ts | 24 +- src/lib/access-keys/create.ts | 24 +- src/lib/access-keys/delete.ts | 20 +- src/lib/access-keys/get.ts | 18 +- src/lib/access-keys/list.ts | 22 +- src/lib/buckets/create.ts | 30 +- src/lib/buckets/delete.ts | 20 +- src/lib/buckets/get.ts | 16 +- src/lib/buckets/list.ts | 18 +- src/lib/buckets/set-cors.ts | 14 +- src/lib/buckets/set-locations.ts | 20 +- src/lib/buckets/set-migration.ts | 14 +- src/lib/buckets/set-notifications.ts | 16 +- src/lib/buckets/set-transition.ts | 16 +- src/lib/buckets/set-ttl.ts | 14 +- src/lib/buckets/set.ts | 16 +- src/lib/configure/index.ts | 38 ++- src/lib/cp.ts | 39 +-- src/lib/credentials/test.ts | 16 +- src/lib/forks/create.ts | 12 +- src/lib/forks/list.ts | 18 +- src/lib/iam/policies/create.ts | 22 +- src/lib/iam/policies/delete.ts | 22 +- src/lib/iam/policies/edit.ts | 26 +- src/lib/iam/policies/get.ts | 22 +- src/lib/iam/policies/list.ts | 22 +- src/lib/iam/users/invite.ts | 20 +- src/lib/iam/users/list.ts | 28 +- src/lib/iam/users/remove.ts | 26 +- src/lib/iam/users/revoke-invitation.ts | 26 +- src/lib/iam/users/update-role.ts | 26 +- src/lib/login/credentials.ts | 40 ++- src/lib/login/oauth.ts | 16 +- src/lib/login/select.ts | 5 +- src/lib/logout.ts | 10 +- src/lib/ls.ts | 10 +- src/lib/mk.ts | 10 +- src/lib/mv.ts | 22 +- src/lib/objects/delete.ts | 20 +- src/lib/objects/get.ts | 20 +- src/lib/objects/list.ts | 16 +- src/lib/objects/put.ts | 20 +- src/lib/objects/set.ts | 14 +- src/lib/organizations/create.ts | 22 +- src/lib/organizations/list.ts | 34 +- src/lib/organizations/select.ts | 20 +- src/lib/presign.ts | 20 +- src/lib/rm.ts | 18 +- src/lib/snapshots/list.ts | 16 +- src/lib/snapshots/take.ts | 12 +- src/lib/stat.ts | 20 +- src/lib/touch.ts | 8 +- src/lib/whoami.ts | 18 +- src/specs-embedded.ts | 3 +- src/utils/bucket-info.ts | 1 + src/utils/exit.ts | 6 +- src/utils/locations.ts | 1 + src/utils/messages.ts | 2 +- src/utils/path.ts | 3 +- src/utils/specs.ts | 5 +- src/utils/update-check.ts | 7 +- test/auth/storage.test.ts | 413 +++++++++++++++++++++++++ tsconfig.json | 6 +- tsup.config.ts | 8 +- vitest.config.ts | 6 + 77 files changed, 1380 insertions(+), 727 deletions(-) delete mode 100644 src/auth/config.ts rename src/auth/{s3-client.ts => provider.ts} (77%) delete mode 100644 src/auth/types.ts create mode 100644 test/auth/storage.test.ts diff --git a/eslint.config.js b/eslint.config.js index 6299c3b..c73d7a0 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,16 +1,24 @@ import eslint from "@eslint/js"; +import simpleImportSort from "eslint-plugin-simple-import-sort"; import tseslint from "typescript-eslint"; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, { + plugins: { "simple-import-sort": simpleImportSort }, rules: { "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/consistent-type-imports": [ + "error", + { prefer: "type-imports", fixStyle: "separate-type-imports" }, + ], + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", "prefer-const": "error", "no-var": "error", }, diff --git a/package-lock.json b/package-lock.json index 86b65a3..e43cd8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "@types/node": "^22.19.11", "dotenv": "^17.3.1", "eslint": "^10.1.0", + "eslint-plugin-simple-import-sort": "^12.1.1", "husky": "^9.1.7", "prettier": "^3.8.1", "publint": "^0.3.18", @@ -6057,6 +6058,16 @@ } } }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", diff --git a/package.json b/package.json index 1e9463b..b47a3e9 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "lint:fix": "eslint src --fix", "format": "prettier --write \"src/**/*.ts\"", "format:check": "prettier --check \"src/**/*.ts\"", - "test": "vitest run test/utils test/cli-core.test.ts test/specs-completeness.test.ts", + "test": "vitest run test/utils test/auth test/cli-core.test.ts test/specs-completeness.test.ts", "test:watch": "vitest", "test:all": "vitest run", "test:integration": "vitest run test/cli.test.ts", @@ -100,6 +100,7 @@ "@types/node": "^22.19.11", "dotenv": "^17.3.1", "eslint": "^10.1.0", + "eslint-plugin-simple-import-sort": "^12.1.1", "husky": "^9.1.7", "prettier": "^3.8.1", "publint": "^0.3.18", diff --git a/src/auth/client.ts b/src/auth/client.ts index 8634448..4b0dccd 100644 --- a/src/auth/client.ts +++ b/src/auth/client.ts @@ -4,25 +4,73 @@ */ import axios from 'axios'; -import open from 'open'; import { createRemoteJWKSet, jwtVerify } from 'jose'; -import type { - TokenSet, - IdTokenClaims, - TigrisNamespace, - TigrisOrg, - OrganizationInfo, -} from './types.js'; -import { getAuth0Config, TIGRIS_CLAIMS_NAMESPACE } from './config.js'; +import open from 'open'; + +import type { OrganizationInfo, TokenSet } from './storage.js'; import { - storeTokens, - getTokens, clearTokens, - storeOrganizations, getOrganizations, + getTokens, storeLoginMethod, + storeOrganizations, + storeTokens, } from './storage.js'; +/** + * Auth0 configuration for CLI authentication + */ +export interface Auth0Config { + domain: string; + clientId: string; + audience: string; +} + +/** + * Get Auth0 configuration from environment variables or defaults + */ +export function getAuth0Config(): Auth0Config { + const isDev = process.env.TIGRIS_ENV === 'development'; + const domain = isDev + ? 'auth-dev.tigris.dev' + : (process.env.AUTH0_DOMAIN ?? 'auth.storage.tigrisdata.io'); + const clientId = isDev + ? 'JdJVYIyw0O1uHi5L5OJH903qaWBgd3gF' + : (process.env.AUTH0_CLIENT_ID ?? 'DMejqeM3CQ4IqTjEcd3oA9eEiT40hn8D'); + const audience = isDev + ? 'https://tigris-api-dev' + : (process.env.AUTH0_AUDIENCE ?? 'https://tigris-os-api'); + + return { domain, clientId, audience }; +} + +/** + * Custom claims namespace for Tigris + */ +export const TIGRIS_CLAIMS_NAMESPACE = + process.env.TIGRIS_CLAIMS_NAMESPACE || 'https://tigris'; + +/** + * OAuth-specific types + */ +export interface IdTokenClaims { + sub: string; + email?: string; + email_verified?: boolean; + [key: string]: unknown; +} + +interface TigrisOrg { + id: string; + name: string; + slug: string; +} + +interface TigrisNamespace { + ns?: (string | TigrisOrg)[]; + organizations?: (string | TigrisOrg)[]; +} + interface DeviceCodeResponse { device_code: string; user_code: string; diff --git a/src/auth/config.ts b/src/auth/config.ts deleted file mode 100644 index 6b54a3d..0000000 --- a/src/auth/config.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - DEFAULT_STORAGE_ENDPOINT, - DEFAULT_IAM_ENDPOINT, - DEFAULT_MGMT_ENDPOINT, -} from '../constants.js'; - -/** - * Auth0 configuration for CLI authentication - */ -export interface Auth0Config { - domain: string; - clientId: string; - audience: string; -} - -export interface TigrisConfig { - endpoint: string; - iamEndpoint: string; - mgmtEndpoint: string; -} - -/** - * Get Auth0 configuration from environment variables or defaults - * In production, these should come from environment variables - * Production Auth0 domain is auth.storage.tigrisdata.io - */ -export function getAuth0Config(): Auth0Config { - const isDev = process.env.TIGRIS_ENV === 'development'; - const domain = isDev - ? 'auth-dev.tigris.dev' - : (process.env.AUTH0_DOMAIN ?? 'auth.storage.tigrisdata.io'); - const clientId = isDev - ? 'JdJVYIyw0O1uHi5L5OJH903qaWBgd3gF' - : (process.env.AUTH0_CLIENT_ID ?? 'DMejqeM3CQ4IqTjEcd3oA9eEiT40hn8D'); - const audience = isDev - ? 'https://tigris-api-dev' - : (process.env.AUTH0_AUDIENCE ?? 'https://tigris-os-api'); - - return { - domain, - clientId, - audience, - }; -} - -/** - * Custom claims namespace for Tigris - */ -export const TIGRIS_CLAIMS_NAMESPACE = - process.env.TIGRIS_CLAIMS_NAMESPACE || 'https://tigris'; - -export function getTigrisConfig(): TigrisConfig { - // If any TIGRIS_ endpoint var is set, use TIGRIS_ vars exclusively - if (process.env.TIGRIS_STORAGE_ENDPOINT || process.env.TIGRIS_IAM_ENDPOINT) { - return { - endpoint: process.env.TIGRIS_STORAGE_ENDPOINT || DEFAULT_STORAGE_ENDPOINT, - iamEndpoint: process.env.TIGRIS_IAM_ENDPOINT || DEFAULT_IAM_ENDPOINT, - mgmtEndpoint: process.env.TIGRIS_MGMT_ENDPOINT || DEFAULT_MGMT_ENDPOINT, - }; - } - - // Fall back to AWS_ vars - return { - endpoint: process.env.AWS_ENDPOINT_URL_S3 || DEFAULT_STORAGE_ENDPOINT, - iamEndpoint: process.env.AWS_ENDPOINT_URL_IAM || DEFAULT_IAM_ENDPOINT, - mgmtEndpoint: process.env.AWS_ENDPOINT_URL_MGMT || DEFAULT_MGMT_ENDPOINT, - }; -} diff --git a/src/auth/fly.ts b/src/auth/fly.ts index c151d42..3f485a3 100644 --- a/src/auth/fly.ts +++ b/src/auth/fly.ts @@ -1,6 +1,7 @@ import axios from 'axios'; -import type { OrganizationInfo } from './types.js'; -import { getAuth0Config, TIGRIS_CLAIMS_NAMESPACE } from './config.js'; + +import { getAuth0Config, TIGRIS_CLAIMS_NAMESPACE } from './client.js'; +import type { OrganizationInfo } from './storage.js'; export function isFlyUser(organizationId?: string): boolean { return !!organizationId?.startsWith('flyio_'); diff --git a/src/auth/s3-client.ts b/src/auth/provider.ts similarity index 77% rename from src/auth/s3-client.ts rename to src/auth/provider.ts index 8bda217..649d14b 100644 --- a/src/auth/s3-client.ts +++ b/src/auth/provider.ts @@ -1,20 +1,48 @@ /** - * S3 Client factory - * Creates appropriate S3 client based on login method + * Auth provider + * Resolves auth method and provides storage config + service endpoints */ import { fromIni } from '@aws-sdk/credential-providers'; + +import { + DEFAULT_IAM_ENDPOINT, + DEFAULT_MGMT_ENDPOINT, + DEFAULT_STORAGE_ENDPOINT, +} from '../constants.js'; +import { getAuth0Config, getAuthClient } from './client.js'; import { + getAwsProfileConfig, + getEnvCredentials, getLoginMethod as getStoredLoginMethod, + getSelectedOrganization, getStoredCredentials, - getEnvCredentials, hasAwsProfile, - getAwsProfileConfig, - getSelectedOrganization, } from './storage.js'; -import { getAuthClient } from './client.js'; -import { getAuth0Config, getTigrisConfig } from './config.js'; -import { DEFAULT_STORAGE_ENDPOINT } from '../constants.js'; + +export interface TigrisConfig { + endpoint: string; + iamEndpoint: string; + mgmtEndpoint: string; +} + +export function getTigrisConfig(): TigrisConfig { + // If any TIGRIS_ endpoint var is set, use TIGRIS_ vars exclusively + if (process.env.TIGRIS_STORAGE_ENDPOINT || process.env.TIGRIS_IAM_ENDPOINT) { + return { + endpoint: process.env.TIGRIS_STORAGE_ENDPOINT || DEFAULT_STORAGE_ENDPOINT, + iamEndpoint: process.env.TIGRIS_IAM_ENDPOINT || DEFAULT_IAM_ENDPOINT, + mgmtEndpoint: process.env.TIGRIS_MGMT_ENDPOINT || DEFAULT_MGMT_ENDPOINT, + }; + } + + // Fall back to AWS_ vars + return { + endpoint: process.env.AWS_ENDPOINT_URL_S3 || DEFAULT_STORAGE_ENDPOINT, + iamEndpoint: process.env.AWS_ENDPOINT_URL_IAM || DEFAULT_IAM_ENDPOINT, + mgmtEndpoint: process.env.AWS_ENDPOINT_URL_MGMT || DEFAULT_MGMT_ENDPOINT, + }; +} const tigrisConfig = getTigrisConfig(); const auth0Config = getAuth0Config(); @@ -34,12 +62,12 @@ async function triggerAutoLogin(): Promise { return true; } -export type LoginMethod = 'oauth' | 'credentials'; - /** * Get the login method used by the user */ -export async function getLoginMethod(): Promise { +export async function getLoginMethod(): Promise< + 'oauth' | 'credentials' | null +> { return getStoredLoginMethod(); } @@ -117,10 +145,13 @@ export async function getStorageConfig(options?: { if (loginMethod === 'credentials') { const loginCredentials = getStoredCredentials(); if (loginCredentials) { + const selectedOrg = getSelectedOrganization(); return { accessKeyId: loginCredentials.accessKeyId, secretAccessKey: loginCredentials.secretAccessKey, endpoint: loginCredentials.endpoint, + organizationId: selectedOrg ?? undefined, + iamEndpoint: tigrisConfig.iamEndpoint, }; } } diff --git a/src/auth/storage.ts b/src/auth/storage.ts index d64bba4..8284d80 100644 --- a/src/auth/storage.ts +++ b/src/auth/storage.ts @@ -2,42 +2,80 @@ * Secure storage using a single config file */ -import { homedir, platform } from 'os'; -import { join } from 'path'; +import { loadSharedConfigFiles } from '@smithy/shared-ini-file-loader'; +import { execFileSync } from 'child_process'; import { - readFileSync, - writeFileSync, + chmodSync, existsSync, mkdirSync, - chmodSync, + readFileSync, + writeFileSync, } from 'fs'; -import { execFileSync } from 'child_process'; -import { loadSharedConfigFiles } from '@smithy/shared-ini-file-loader'; -import type { TokenSet, OrganizationInfo } from './types.js'; +import { homedir, platform } from 'os'; +import { join } from 'path'; + import { DEFAULT_STORAGE_ENDPOINT } from '../constants.js'; -const CONFIG_DIR = join(homedir(), '.tigris'); -const CONFIG_FILE = join(CONFIG_DIR, 'config.json'); +export interface TokenSet { + accessToken: string; + refreshToken?: string; + idToken?: string; + expiresAt: number; // Unix timestamp +} + +export interface OrganizationInfo { + id: string; + name: string; + displayName?: string; +} /** - * Configuration structure + * Credentials configuration interface (public — callers use this shape) */ -interface TigrisConfig { - tokens?: TokenSet; - organizations?: OrganizationInfo[]; - selectedOrganization?: string; - credentials?: CredentialsConfig; - temporaryCredentials?: CredentialsConfig; - loginMethod?: 'oauth' | 'credentials'; +export interface CredentialsConfig { + accessKeyId: string; + secretAccessKey: string; + endpoint: string; } /** - * Credentials configuration interface + * Stored credential with optional organization */ -export interface CredentialsConfig { +interface StoredCredential { accessKeyId: string; secretAccessKey: string; endpoint: string; + organizationId?: string; +} + +/** + * V2 configuration structure — config is nested by auth method + */ +interface TigrisConfigV2 { + version: 2; + activeMethod?: 'oauth' | 'credentials'; + oauth?: { + tokens?: TokenSet; + organizations?: OrganizationInfo[]; + selectedOrganization?: string; + }; + credentials?: { + saved?: StoredCredential; + temporary?: StoredCredential; + }; +} + +const CONFIG_DIR = join(homedir(), '.tigris'); +const CONFIG_FILE = join(CONFIG_DIR, 'config.json'); + +// Exported for tests +export { CONFIG_DIR, CONFIG_FILE }; + +/** + * Type guard — checks that a value is a non-null object (Record-like) + */ +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); } /** @@ -82,35 +120,94 @@ function ensureConfigDir(): void { } /** - * Read config from file + * Migrate v1 config to v2. + * Preserves saved credentials; discards everything else (tokens, orgs, loginMethod). + * User will need to re-login after migration. */ -function readConfig(): TigrisConfig { - if (existsSync(CONFIG_FILE)) { - try { - const data = readFileSync(CONFIG_FILE, 'utf8'); - return JSON.parse(data); - } catch { - return {}; +function migrateV1(raw: Record): TigrisConfigV2 { + const config: TigrisConfigV2 = { version: 2 }; + + // Preserve saved credentials if they look valid + const creds = raw['credentials']; + if ( + isRecord(creds) && + typeof creds['accessKeyId'] === 'string' && + typeof creds['secretAccessKey'] === 'string' && + typeof creds['endpoint'] === 'string' + ) { + config.credentials = { + saved: { + accessKeyId: creds['accessKeyId'], + secretAccessKey: creds['secretAccessKey'], + endpoint: creds['endpoint'], + }, + }; + } + + return config; +} + +/** + * Read config from file, migrating v1 → v2 if needed + */ +function readConfig(): TigrisConfigV2 { + if (!existsSync(CONFIG_FILE)) { + return { version: 2 }; + } + + try { + const data = readFileSync(CONFIG_FILE, 'utf8'); + const parsed: unknown = JSON.parse(data); + + if (!isRecord(parsed)) { + return { version: 2 }; + } + + // Already v2 + if (parsed['version'] === 2) { + return parsed as unknown as TigrisConfigV2; } + + // v1 → v2 migration + const migrated = migrateV1(parsed); + // Write migrated config back to disk + writeConfigSync(migrated); + return migrated; + } catch { + return { version: 2 }; } - return {}; } /** - * Write config to file + * Write config to file (sync, used by migration) */ -async function writeConfig(config: TigrisConfig): Promise { +function writeConfigSync(config: TigrisConfigV2): void { ensureConfigDir(); writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 }); restrictPermissions(CONFIG_FILE, 0o600); } +/** + * Write config to file + */ +async function writeConfig(config: TigrisConfigV2): Promise { + writeConfigSync(config); +} + +// --------------------------------------------------------------------------- +// OAuth data accessors +// --------------------------------------------------------------------------- + /** * Store tokens securely */ export async function storeTokens(tokens: TokenSet): Promise { const config = readConfig(); - config.tokens = tokens; + if (!config.oauth) { + config.oauth = { tokens, organizations: [] }; + } else { + config.oauth.tokens = tokens; + } await writeConfig(config); } @@ -119,7 +216,7 @@ export async function storeTokens(tokens: TokenSet): Promise { */ export async function getTokens(): Promise { const config = readConfig(); - return config.tokens || null; + return config.oauth?.tokens ?? null; } /** @@ -127,7 +224,9 @@ export async function getTokens(): Promise { */ export async function clearTokens(): Promise { const config = readConfig(); - delete config.tokens; + if (config.oauth) { + delete config.oauth.tokens; + } await writeConfig(config); } @@ -138,7 +237,11 @@ export async function storeOrganizations( organizations: OrganizationInfo[] ): Promise { const config = readConfig(); - config.organizations = organizations; + if (!config.oauth) { + config.oauth = { organizations }; + } else { + config.oauth.organizations = organizations; + } await writeConfig(config); } @@ -147,26 +250,54 @@ export async function storeOrganizations( */ export function getOrganizations(): OrganizationInfo[] { const config = readConfig(); - return config.organizations || []; + return config.oauth?.organizations ?? []; } +// --------------------------------------------------------------------------- +// Selected organization — method-aware +// --------------------------------------------------------------------------- + /** - * Store selected organization + * Store selected organization (branches on activeMethod) */ export async function storeSelectedOrganization(orgId: string): Promise { const config = readConfig(); - config.selectedOrganization = orgId; + + if (config.activeMethod === 'credentials') { + // Write to the active credential slot + const slot = config.credentials?.temporary ?? config.credentials?.saved; + if (slot) { + slot.organizationId = orgId; + } + } else { + // Default: write to oauth + if (!config.oauth) { + config.oauth = { organizations: [] }; + } + config.oauth.selectedOrganization = orgId; + } + await writeConfig(config); } /** - * Get selected organization + * Get selected organization (branches on activeMethod) */ export function getSelectedOrganization(): string | null { const config = readConfig(); - return config.selectedOrganization || null; + + if (config.activeMethod === 'credentials') { + const slot = config.credentials?.temporary ?? config.credentials?.saved; + return slot?.organizationId ?? null; + } + + return config.oauth?.selectedOrganization ?? null; } +// --------------------------------------------------------------------------- +// Credential accessors +// --------------------------------------------------------------------------- + /** * Get credentials from environment variables. * If any TIGRIS_ var is set, use TIGRIS_ vars exclusively. @@ -255,15 +386,15 @@ export async function getAwsProfileConfig( * 2. Temporary credentials (from 'tigris login') * 3. Saved credentials (from 'tigris configure') * - * Note: AWS profile and login method checks are handled in s3-client.ts - * Full resolution order (in s3-client): AWS_PROFILE → login → env vars → configured + * Note: AWS profile and login method checks are handled in provider.ts + * Full resolution order (in provider): AWS_PROFILE → login → env vars → configured */ export function getCredentials(): CredentialsConfig | null { const config = readConfig(); return ( getEnvCredentials() || - config.temporaryCredentials || - config.credentials || + toCredentialsConfig(config.credentials?.temporary) || + toCredentialsConfig(config.credentials?.saved) || null ); } @@ -275,15 +406,11 @@ export function getCredentials(): CredentialsConfig | null { */ export function getStoredCredentials(): CredentialsConfig | null { const config = readConfig(); - return config.temporaryCredentials || config.credentials || null; -} - -/** - * Get only permanent/saved credentials (from configure command) - */ -export function getSavedCredentials(): CredentialsConfig | null { - const config = readConfig(); - return config.credentials || null; + return ( + toCredentialsConfig(config.credentials?.temporary) || + toCredentialsConfig(config.credentials?.saved) || + null + ); } /** @@ -293,7 +420,14 @@ export async function storeCredentials( credentials: CredentialsConfig ): Promise { const config = readConfig(); - config.credentials = credentials; + if (!config.credentials) { + config.credentials = {}; + } + config.credentials.saved = { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + endpoint: credentials.endpoint, + }; await writeConfig(config); } @@ -304,16 +438,14 @@ export async function storeTemporaryCredentials( credentials: CredentialsConfig ): Promise { const config = readConfig(); - config.temporaryCredentials = credentials; - await writeConfig(config); -} - -/** - * Clear temporary credentials - */ -export async function clearTemporaryCredentials(): Promise { - const config = readConfig(); - delete config.temporaryCredentials; + if (!config.credentials) { + config.credentials = {}; + } + config.credentials.temporary = { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + endpoint: credentials.endpoint, + }; await writeConfig(config); } @@ -322,7 +454,9 @@ export async function clearTemporaryCredentials(): Promise { */ export async function clearCredentials(): Promise { const config = readConfig(); - delete config.credentials; + if (config.credentials) { + delete config.credentials.saved; + } await writeConfig(config); } @@ -333,7 +467,7 @@ export async function storeLoginMethod( method: 'oauth' | 'credentials' ): Promise { const config = readConfig(); - config.loginMethod = method; + config.activeMethod = method; await writeConfig(config); } @@ -342,7 +476,22 @@ export async function storeLoginMethod( */ export function getLoginMethod(): 'oauth' | 'credentials' | null { const config = readConfig(); - return config.loginMethod || null; + return config.activeMethod ?? null; +} + +/** + * Store organizationId on the active credential slot + * (temporary if it exists, else saved) + */ +export async function storeCredentialOrganization( + orgId: string +): Promise { + const config = readConfig(); + const slot = config.credentials?.temporary ?? config.credentials?.saved; + if (slot) { + slot.organizationId = orgId; + await writeConfig(config); + } } /** @@ -350,11 +499,28 @@ export function getLoginMethod(): 'oauth' | 'credentials' | null { */ export async function clearAllData(): Promise { const config = readConfig(); - const savedCredentials = config.credentials; + const savedCredentials = config.credentials?.saved; - // Clear everything except saved credentials - // This includes: tokens, organizations, selectedOrganization, temporaryCredentials, loginMethod await writeConfig({ - credentials: savedCredentials, + version: 2, + credentials: savedCredentials ? { saved: savedCredentials } : undefined, }); } + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/** + * Convert StoredCredential → CredentialsConfig (strip organizationId) + */ +function toCredentialsConfig( + stored: StoredCredential | undefined +): CredentialsConfig | null { + if (!stored) return null; + return { + accessKeyId: stored.accessKeyId, + secretAccessKey: stored.secretAccessKey, + endpoint: stored.endpoint, + }; +} diff --git a/src/auth/types.ts b/src/auth/types.ts deleted file mode 100644 index 86a86df..0000000 --- a/src/auth/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Authentication types - */ - -export interface TokenSet { - accessToken: string; - refreshToken?: string; - idToken?: string; - expiresAt: number; // Unix timestamp -} - -export interface IdTokenClaims { - sub: string; - email?: string; - email_verified?: boolean; - [key: string]: unknown; -} - -export interface TigrisOrg { - id: string; - name: string; - slug: string; -} - -export interface TigrisNamespace { - ns?: (string | TigrisOrg)[]; - organizations?: (string | TigrisOrg)[]; -} - -export interface OrganizationInfo { - id: string; - name: string; - displayName?: string; -} diff --git a/src/cli-binary.ts b/src/cli-binary.ts index 9df8cc9..7afac9b 100644 --- a/src/cli-binary.ts +++ b/src/cli-binary.ts @@ -5,17 +5,17 @@ (globalThis as { __TIGRIS_BINARY?: boolean }).__TIGRIS_BINARY = true; -import { loadSpecs } from './specs-embedded.js'; -import { setSpecs } from './utils/specs.js'; -import { commandRegistry } from './command-registry.js'; -import { checkForUpdates } from './utils/update-check.js'; import { version } from '../package.json'; import { - setupErrorHandlers, createProgram, - type ModuleLoader, type ImplementationChecker, + type ModuleLoader, + setupErrorHandlers, } from './cli-core.js'; +import { commandRegistry } from './command-registry.js'; +import { loadSpecs } from './specs-embedded.js'; +import { setSpecs } from './utils/specs.js'; +import { checkForUpdates } from './utils/update-check.js'; // Pre-populate the shared specs cache so command modules work without filesystem access const specs = loadSpecs(); diff --git a/src/cli-core.ts b/src/cli-core.ts index a128486..2edf6fc 100644 --- a/src/cli-core.ts +++ b/src/cli-core.ts @@ -2,10 +2,11 @@ * Shared CLI core functionality used by both cli.ts (npm) and cli-binary.ts (binary) */ +import { exitWithError } from '@utils/exit.js'; +import { printDeprecated } from '@utils/messages.js'; import { Command as CommanderCommand } from 'commander'; + import type { Argument, CommandSpec, Specs } from './types.js'; -import { printDeprecated } from './utils/messages.js'; -import { exitWithError } from './utils/exit.js'; export interface ModuleLoader { (commandPath: string[]): Promise<{ diff --git a/src/cli.ts b/src/cli.ts index 1dd2cb6..93ffdb7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,17 +1,18 @@ #!/usr/bin/env node import { existsSync } from 'fs'; -import { join, dirname } from 'path'; +import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; -import { loadSpecs } from './utils/specs.js'; -import { checkForUpdates } from './utils/update-check.js'; + import { version } from '../package.json'; import { - setupErrorHandlers, createProgram, - type ModuleLoader, type ImplementationChecker, + type ModuleLoader, + setupErrorHandlers, } from './cli-core.js'; +import { loadSpecs } from './utils/specs.js'; +import { checkForUpdates } from './utils/update-check.js'; setupErrorHandlers(); diff --git a/src/lib/access-keys/assign.ts b/src/lib/access-keys/assign.ts index c35bdc1..b171a76 100644 --- a/src/lib/access-keys/assign.ts +++ b/src/lib/access-keys/assign.ts @@ -1,20 +1,20 @@ -import { getOption } from '../../utils/options.js'; -import { getLoginMethod } from '../../auth/s3-client.js'; -import { getAuthClient } from '../../auth/client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { getTigrisConfig } from '../../auth/config.js'; import { assignBucketRoles, revokeAllBucketRoles } from '@tigrisdata/iam'; -import { - printStart, - printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; import { exitWithError, getSuccessNextActions, printNextActions, -} from '../../utils/exit.js'; +} from '@utils/exit.js'; +import { + msg, + printFailure, + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; const context = msg('access-keys', 'assign'); diff --git a/src/lib/access-keys/create.ts b/src/lib/access-keys/create.ts index 3d06c85..54442e1 100644 --- a/src/lib/access-keys/create.ts +++ b/src/lib/access-keys/create.ts @@ -1,20 +1,20 @@ -import { getOption } from '../../utils/options.js'; -import { getLoginMethod } from '../../auth/s3-client.js'; -import { getAuthClient } from '../../auth/client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { getTigrisConfig } from '../../auth/config.js'; import { createAccessKey } from '@tigrisdata/iam'; -import { - printStart, - printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; import { exitWithError, getSuccessNextActions, printNextActions, -} from '../../utils/exit.js'; +} from '@utils/exit.js'; +import { + msg, + printFailure, + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; const context = msg('access-keys', 'create'); diff --git a/src/lib/access-keys/delete.ts b/src/lib/access-keys/delete.ts index f5305fb..8cd763a 100644 --- a/src/lib/access-keys/delete.ts +++ b/src/lib/access-keys/delete.ts @@ -1,17 +1,17 @@ -import { getOption } from '../../utils/options.js'; -import { getLoginMethod } from '../../auth/s3-client.js'; -import { getAuthClient } from '../../auth/client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { getTigrisConfig } from '../../auth/config.js'; import { removeAccessKey } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; -import { requireInteractive, confirm } from '../../utils/interactive.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; const context = msg('access-keys', 'delete'); diff --git a/src/lib/access-keys/get.ts b/src/lib/access-keys/get.ts index 61255f1..57195cd 100644 --- a/src/lib/access-keys/get.ts +++ b/src/lib/access-keys/get.ts @@ -1,16 +1,16 @@ -import { getOption } from '../../utils/options.js'; -import { getLoginMethod } from '../../auth/s3-client.js'; -import { getAuthClient } from '../../auth/client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { getTigrisConfig } from '../../auth/config.js'; import { getAccessKey } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; const context = msg('access-keys', 'get'); diff --git a/src/lib/access-keys/list.ts b/src/lib/access-keys/list.ts index cf5be84..eb25399 100644 --- a/src/lib/access-keys/list.ts +++ b/src/lib/access-keys/list.ts @@ -1,18 +1,18 @@ -import { formatOutput } from '../../utils/format.js'; -import { getOption } from '../../utils/options.js'; -import { getLoginMethod } from '../../auth/s3-client.js'; -import { getAuthClient } from '../../auth/client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { getTigrisConfig } from '../../auth/config.js'; import { listAccessKeys } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; const context = msg('access-keys', 'list'); diff --git a/src/lib/buckets/create.ts b/src/lib/buckets/create.ts index 22e6ff7..325a73f 100644 --- a/src/lib/buckets/create.ts +++ b/src/lib/buckets/create.ts @@ -1,22 +1,22 @@ -import { getOption } from '../../utils/options'; -import enquirer from 'enquirer'; -import { requireInteractive } from '../../utils/interactive.js'; -import { getArgumentSpec, buildPromptChoices } from '../../utils/specs.js'; -import { StorageClass, createBucket } from '@tigrisdata/storage'; -import { getStorageConfig } from '../../auth/s3-client'; -import { parseLocations, promptLocations } from '../../utils/locations.js'; -import type { BucketLocations } from '@tigrisdata/storage'; -import { - printStart, - printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; +import { getStorageConfig } from '@auth/provider.js'; +import type { BucketLocations, StorageClass } from '@tigrisdata/storage'; +import { createBucket } from '@tigrisdata/storage'; import { exitWithError, getSuccessNextActions, printNextActions, -} from '../../utils/exit.js'; +} from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; +import { parseLocations, promptLocations } from '@utils/locations.js'; +import { + msg, + printFailure, + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; +import { buildPromptChoices, getArgumentSpec } from '@utils/specs.js'; +import enquirer from 'enquirer'; const { prompt } = enquirer; diff --git a/src/lib/buckets/delete.ts b/src/lib/buckets/delete.ts index f4ffd27..69948c5 100644 --- a/src/lib/buckets/delete.ts +++ b/src/lib/buckets/delete.ts @@ -1,18 +1,18 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { removeBucket } from '@tigrisdata/storage'; -import { - printStart, - printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { requireInteractive, confirm } from '../../utils/interactive.js'; import { exitWithError, getSuccessNextActions, printNextActions, -} from '../../utils/exit.js'; +} from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; +import { + msg, + printFailure, + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'delete'); diff --git a/src/lib/buckets/get.ts b/src/lib/buckets/get.ts index 9186758..b5cd779 100644 --- a/src/lib/buckets/get.ts +++ b/src/lib/buckets/get.ts @@ -1,15 +1,15 @@ -import { getOption } from '../../utils/options.js'; -import { formatOutput } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { getBucketInfo } from '@tigrisdata/storage'; +import { buildBucketInfo } from '@utils/bucket-info.js'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { buildBucketInfo } from '../../utils/bucket-info.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'get'); diff --git a/src/lib/buckets/list.ts b/src/lib/buckets/list.ts index 5fd362a..bf2bf6c 100644 --- a/src/lib/buckets/list.ts +++ b/src/lib/buckets/list.ts @@ -1,15 +1,15 @@ -import { getOption } from '../../utils/options.js'; -import { formatOutput } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { listBuckets, getBucketInfo } from '@tigrisdata/storage'; +import { getStorageConfig } from '@auth/provider.js'; +import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'list'); diff --git a/src/lib/buckets/set-cors.ts b/src/lib/buckets/set-cors.ts index 4fe3f35..f714361 100644 --- a/src/lib/buckets/set-cors.ts +++ b/src/lib/buckets/set-cors.ts @@ -1,14 +1,14 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { setBucketCors } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-cors'); diff --git a/src/lib/buckets/set-locations.ts b/src/lib/buckets/set-locations.ts index 4cf0d1b..782c634 100644 --- a/src/lib/buckets/set-locations.ts +++ b/src/lib/buckets/set-locations.ts @@ -1,17 +1,17 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { updateBucket } from '@tigrisdata/storage'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import type { BucketLocations } from '@tigrisdata/storage'; -import { parseLocations, promptLocations } from '../../utils/locations.js'; -import { requireInteractive } from '../../utils/interactive.js'; +import { updateBucket } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; +import { parseLocations, promptLocations } from '@utils/locations.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-locations'); diff --git a/src/lib/buckets/set-migration.ts b/src/lib/buckets/set-migration.ts index 6e79bde..c5ca522 100644 --- a/src/lib/buckets/set-migration.ts +++ b/src/lib/buckets/set-migration.ts @@ -1,14 +1,14 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { setBucketMigration } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-migration'); diff --git a/src/lib/buckets/set-notifications.ts b/src/lib/buckets/set-notifications.ts index dc94926..758c587 100644 --- a/src/lib/buckets/set-notifications.ts +++ b/src/lib/buckets/set-notifications.ts @@ -1,17 +1,17 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { - setBucketNotifications, type BucketNotification, + setBucketNotifications, } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-notifications'); diff --git a/src/lib/buckets/set-transition.ts b/src/lib/buckets/set-transition.ts index ad92439..39213cd 100644 --- a/src/lib/buckets/set-transition.ts +++ b/src/lib/buckets/set-transition.ts @@ -1,17 +1,17 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { - setBucketLifecycle, type BucketLifecycleRule, + setBucketLifecycle, } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-transition'); diff --git a/src/lib/buckets/set-ttl.ts b/src/lib/buckets/set-ttl.ts index a186864..5db5a95 100644 --- a/src/lib/buckets/set-ttl.ts +++ b/src/lib/buckets/set-ttl.ts @@ -1,14 +1,14 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { setBucketTtl } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-ttl'); diff --git a/src/lib/buckets/set.ts b/src/lib/buckets/set.ts index 720fb9b..989dacc 100644 --- a/src/lib/buckets/set.ts +++ b/src/lib/buckets/set.ts @@ -1,15 +1,15 @@ -import { getOption, parseBoolean } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { updateBucket, type UpdateBucketOptions } from '@tigrisdata/storage'; -import { parseLocations } from '../../utils/locations.js'; +import { exitWithError } from '@utils/exit.js'; +import { parseLocations } from '@utils/locations.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption, parseBoolean } from '@utils/options.js'; const context = msg('buckets', 'set'); diff --git a/src/lib/configure/index.ts b/src/lib/configure/index.ts index 45ffba4..592e82e 100644 --- a/src/lib/configure/index.ts +++ b/src/lib/configure/index.ts @@ -1,15 +1,22 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive } from '../../utils/interactive.js'; -import { storeCredentials, storeLoginMethod } from '../../auth/storage.js'; -import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { + storeCredentialOrganization, + storeCredentials, + storeLoginMethod, +} from '@auth/storage.js'; +import { whoami } from '@tigrisdata/iam'; +import { exitWithError, printNextActions } from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; +} from '@utils/messages.js'; + +import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; const context = msg('configure'); @@ -93,6 +100,23 @@ export default async function configure(options: Record) { // Store login method await storeLoginMethod('credentials'); + // Fetch and store organizationId from whoami (best-effort) + try { + const tigrisConfig = getTigrisConfig(); + const { data } = await whoami({ + config: { + accessKeyId: accessKey as string, + secretAccessKey: accessSecret as string, + iamEndpoint: tigrisConfig.iamEndpoint, + }, + }); + if (data?.organizationId) { + await storeCredentialOrganization(data.organizationId); + } + } catch { + // Non-fatal — org will just be missing + } + printSuccess(context); printNextActions(context); } catch (error) { diff --git a/src/lib/cp.ts b/src/lib/cp.ts index 6b69913..64692e7 100644 --- a/src/lib/cp.ts +++ b/src/lib/cp.ts @@ -1,31 +1,32 @@ +import { getStorageConfig } from '@auth/provider.js'; +import { get, head, list, put } from '@tigrisdata/storage'; +import { executeWithConcurrency } from '@utils/concurrency.js'; +import { exitWithError } from '@utils/exit.js'; +import { formatSize } from '@utils/format.js'; +import { getOption } from '@utils/options.js'; +import { + globToRegex, + isPathFolder, + isRemotePath, + listAllItems, + parseRemotePath, + wildcardPrefix, +} from '@utils/path.js'; +import { calculateUploadParams } from '@utils/upload.js'; import { createReadStream, createWriteStream, - statSync, - readdirSync, - mkdirSync, existsSync, + mkdirSync, + readdirSync, + statSync, } from 'fs'; -import { resolve, dirname, basename, join, relative } from 'path'; import { homedir } from 'os'; +import { basename, dirname, join, relative, resolve } from 'path'; import { Readable } from 'stream'; import { pipeline } from 'stream/promises'; -import { - isRemotePath, - parseRemotePath, - isPathFolder, - listAllItems, - globToRegex, - wildcardPrefix, -} from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig } from '../auth/s3-client.js'; -import { formatSize } from '../utils/format.js'; -import { get, put, list, head } from '@tigrisdata/storage'; -import { executeWithConcurrency } from '../utils/concurrency.js'; -import { calculateUploadParams } from '../utils/upload.js'; + import type { ParsedPath } from '../types.js'; -import { exitWithError } from '../utils/exit.js'; let _jsonMode = false; diff --git a/src/lib/credentials/test.ts b/src/lib/credentials/test.ts index 2db94ef..70baefc 100644 --- a/src/lib/credentials/test.ts +++ b/src/lib/credentials/test.ts @@ -1,14 +1,14 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getSelectedOrganization } from '../../auth/storage.js'; -import { listBuckets, getBucketInfo } from '@tigrisdata/storage'; +import { getStorageConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; +import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('credentials', 'test'); diff --git a/src/lib/forks/create.ts b/src/lib/forks/create.ts index b8b5d76..396b51e 100644 --- a/src/lib/forks/create.ts +++ b/src/lib/forks/create.ts @@ -1,13 +1,13 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { createBucket } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('forks', 'create'); diff --git a/src/lib/forks/list.ts b/src/lib/forks/list.ts index 05d9e1d..17a276f 100644 --- a/src/lib/forks/list.ts +++ b/src/lib/forks/list.ts @@ -1,15 +1,15 @@ -import { getOption } from '../../utils/options.js'; -import { formatOutput } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; -import { listBuckets, getBucketInfo } from '@tigrisdata/storage'; +import { getStorageConfig } from '@auth/provider.js'; +import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('forks', 'list'); diff --git a/src/lib/iam/policies/create.ts b/src/lib/iam/policies/create.ts index 606a484..66c5445 100644 --- a/src/lib/iam/policies/create.ts +++ b/src/lib/iam/policies/create.ts @@ -1,18 +1,20 @@ import { existsSync, readFileSync } from 'node:fs'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; + +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { addPolicy, type PolicyDocument } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; -import { readStdin, parseDocument } from './utils.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { parseDocument, readStdin } from './utils.js'; const context = msg('iam policies', 'create'); diff --git a/src/lib/iam/policies/delete.ts b/src/lib/iam/policies/delete.ts index 806462d..a4a3bd2 100644 --- a/src/lib/iam/policies/delete.ts +++ b/src/lib/iam/policies/delete.ts @@ -1,20 +1,20 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive, confirm } from '../../../utils/interactive.js'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { deletePolicy, listPolicies } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'delete'); diff --git a/src/lib/iam/policies/edit.ts b/src/lib/iam/policies/edit.ts index c39a6b8..101598c 100644 --- a/src/lib/iam/policies/edit.ts +++ b/src/lib/iam/policies/edit.ts @@ -1,27 +1,29 @@ import { existsSync, readFileSync } from 'node:fs'; + import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive } from '../../../utils/interactive.js'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { editPolicy, getPolicy, listPolicies, type PolicyDocument, } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; -import { readStdin, parseDocument } from './utils.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; + +import { parseDocument, readStdin } from './utils.js'; const context = msg('iam policies', 'edit'); diff --git a/src/lib/iam/policies/get.ts b/src/lib/iam/policies/get.ts index 6f67977..fd51cfb 100644 --- a/src/lib/iam/policies/get.ts +++ b/src/lib/iam/policies/get.ts @@ -1,20 +1,20 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getOption } from '../../../utils/options.js'; -import { formatOutput } from '../../../utils/format.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { getPolicy, listPolicies } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'get'); diff --git a/src/lib/iam/policies/list.ts b/src/lib/iam/policies/list.ts index 90cae64..5b810d3 100644 --- a/src/lib/iam/policies/list.ts +++ b/src/lib/iam/policies/list.ts @@ -1,18 +1,18 @@ -import { getOption } from '../../../utils/options.js'; -import { formatOutput } from '../../../utils/format.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { listPolicies } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'list'); diff --git a/src/lib/iam/users/invite.ts b/src/lib/iam/users/invite.ts index 539f7cb..5025d81 100644 --- a/src/lib/iam/users/invite.ts +++ b/src/lib/iam/users/invite.ts @@ -1,17 +1,17 @@ -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; -import { isFlyUser } from '../../../auth/fly.js'; +import { getAuthClient } from '@auth/client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { inviteUser } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam users', 'invite'); diff --git a/src/lib/iam/users/list.ts b/src/lib/iam/users/list.ts index 71800fe..17294af 100644 --- a/src/lib/iam/users/list.ts +++ b/src/lib/iam/users/list.ts @@ -1,24 +1,24 @@ -import { getOption } from '../../../utils/options.js'; +import { getAuthClient } from '@auth/client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; +import { listUsers } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; import { formatJson, - formatXml, formatTable, + formatXml, type TableColumn, -} from '../../../utils/format.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; -import { isFlyUser } from '../../../auth/fly.js'; -import { listUsers } from '@tigrisdata/iam'; +} from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam users', 'list'); diff --git a/src/lib/iam/users/remove.ts b/src/lib/iam/users/remove.ts index a000b62..c50bb55 100644 --- a/src/lib/iam/users/remove.ts +++ b/src/lib/iam/users/remove.ts @@ -1,21 +1,21 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive, confirm } from '../../../utils/interactive.js'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; -import { isFlyUser } from '../../../auth/fly.js'; -import { removeUser as removeUserFromOrg, listUsers } from '@tigrisdata/iam'; +import { getAuthClient } from '@auth/client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; +import { listUsers, removeUser as removeUserFromOrg } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam users', 'remove'); diff --git a/src/lib/iam/users/revoke-invitation.ts b/src/lib/iam/users/revoke-invitation.ts index 03def7b..71d5c90 100644 --- a/src/lib/iam/users/revoke-invitation.ts +++ b/src/lib/iam/users/revoke-invitation.ts @@ -1,21 +1,21 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive, confirm } from '../../../utils/interactive.js'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; -import { isFlyUser } from '../../../auth/fly.js'; -import { revokeInvitation as revokeInv, listUsers } from '@tigrisdata/iam'; +import { getAuthClient } from '@auth/client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; +import { listUsers, revokeInvitation as revokeInv } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam users', 'revoke-invitation'); diff --git a/src/lib/iam/users/update-role.ts b/src/lib/iam/users/update-role.ts index 8c0a54a..56bc63e 100644 --- a/src/lib/iam/users/update-role.ts +++ b/src/lib/iam/users/update-role.ts @@ -1,21 +1,21 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive } from '../../../utils/interactive.js'; -import { getOption } from '../../../utils/options.js'; -import { getLoginMethod } from '../../../auth/s3-client.js'; -import { getAuthClient } from '../../../auth/client.js'; -import { getSelectedOrganization } from '../../../auth/storage.js'; -import { getTigrisConfig } from '../../../auth/config.js'; -import { isFlyUser } from '../../../auth/fly.js'; -import { updateUserRole, listUsers } from '@tigrisdata/iam'; +import { getAuthClient } from '@auth/client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getLoginMethod } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; +import { listUsers, updateUserRole } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../../utils/messages.js'; -import { exitWithError } from '../../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('iam users', 'update-role'); diff --git a/src/lib/login/credentials.ts b/src/lib/login/credentials.ts index 8472d1d..8ab7ad7 100644 --- a/src/lib/login/credentials.ts +++ b/src/lib/login/credentials.ts @@ -1,19 +1,23 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive } from '../../utils/interactive.js'; +import { getTigrisConfig } from '@auth/provider.js'; import { - getSavedCredentials, + getStoredCredentials, + storeCredentialOrganization, storeLoginMethod, storeTemporaryCredentials, -} from '../../auth/storage.js'; -import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; +} from '@auth/storage.js'; +import { whoami } from '@tigrisdata/iam'; +import { exitWithError, printNextActions } from '@utils/exit.js'; +import { requireInteractive } from '@utils/interactive.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; +} from '@utils/messages.js'; + +import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; const context = msg('login', 'credentials'); @@ -78,7 +82,7 @@ export default async function credentials(options: Record) { } // Get endpoint: configured → default - const configuredCreds = getSavedCredentials(); + const configuredCreds = getStoredCredentials(); const endpoint = configuredCreds?.endpoint || DEFAULT_STORAGE_ENDPOINT; // Store as temporary credentials (cleared on logout) @@ -89,6 +93,24 @@ export default async function credentials(options: Record) { }); await storeLoginMethod('credentials'); + + // Fetch and store organizationId from whoami (best-effort) + try { + const tigrisConfig = getTigrisConfig(); + const { data } = await whoami({ + config: { + accessKeyId: accessKey as string, + secretAccessKey: accessSecret as string, + iamEndpoint: tigrisConfig.iamEndpoint, + }, + }); + if (data?.organizationId) { + await storeCredentialOrganization(data.organizationId); + } + } catch { + // Non-fatal — org will just be missing + } + printSuccess(context); printNextActions(context); } diff --git a/src/lib/login/oauth.ts b/src/lib/login/oauth.ts index edb1f96..0b0424f 100644 --- a/src/lib/login/oauth.ts +++ b/src/lib/login/oauth.ts @@ -1,14 +1,14 @@ -import { getAuthClient } from '../../auth/client.js'; -import { storeSelectedOrganization } from '../../auth/storage.js'; +import { getAuthClient } from '@auth/client.js'; +import { storeSelectedOrganization } from '@auth/storage.js'; +import { exitWithError, printNextActions } from '@utils/exit.js'; import { - printStart, - printSuccess, - printFailure, + msg, printAlreadyDone, + printFailure, printHint, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; + printStart, + printSuccess, +} from '@utils/messages.js'; const context = msg('login', 'oauth'); diff --git a/src/lib/login/select.ts b/src/lib/login/select.ts index bc2ca7b..3b133f7 100644 --- a/src/lib/login/select.ts +++ b/src/lib/login/select.ts @@ -1,8 +1,9 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { requireInteractive } from '../../utils/interactive.js'; -import { oauth } from './oauth.js'; +import { requireInteractive } from '@utils/interactive.js'; + import credentials from './credentials.js'; +import { oauth } from './oauth.js'; /** * Main login command diff --git a/src/lib/logout.ts b/src/lib/logout.ts index b26e4d7..cb43b22 100644 --- a/src/lib/logout.ts +++ b/src/lib/logout.ts @@ -1,11 +1,11 @@ -import { clearAllData } from '../auth/storage.js'; +import { clearAllData } from '@auth/storage.js'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../utils/messages.js'; -import { exitWithError } from '../utils/exit.js'; +} from '@utils/messages.js'; const context = msg('logout'); diff --git a/src/lib/ls.ts b/src/lib/ls.ts index ae5ddcc..4b97500 100644 --- a/src/lib/ls.ts +++ b/src/lib/ls.ts @@ -1,9 +1,9 @@ -import { parseAnyPath } from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { formatOutput, formatSize } from '../utils/format.js'; -import { getStorageConfig } from '../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { list, listBuckets } from '@tigrisdata/storage'; -import { exitWithError } from '../utils/exit.js'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput, formatSize } from '@utils/format.js'; +import { getOption } from '@utils/options.js'; +import { parseAnyPath } from '@utils/path.js'; export default async function ls(options: Record) { const pathString = getOption(options, ['path']); diff --git a/src/lib/mk.ts b/src/lib/mk.ts index 6fe0200..266e19d 100644 --- a/src/lib/mk.ts +++ b/src/lib/mk.ts @@ -1,9 +1,9 @@ -import { parseAnyPath } from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig } from '../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { createBucket, put, type StorageClass } from '@tigrisdata/storage'; -import { parseLocations } from '../utils/locations.js'; -import { exitWithError } from '../utils/exit.js'; +import { exitWithError } from '@utils/exit.js'; +import { parseLocations } from '@utils/locations.js'; +import { getOption } from '@utils/options.js'; +import { parseAnyPath } from '@utils/path.js'; export default async function mk(options: Record) { const pathString = getOption(options, ['path']); diff --git a/src/lib/mv.ts b/src/lib/mv.ts index e7e5d90..9de8ef3 100644 --- a/src/lib/mv.ts +++ b/src/lib/mv.ts @@ -1,18 +1,18 @@ +import { getStorageConfig } from '@auth/provider.js'; +import { get, head, list, put, remove } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatSize } from '@utils/format.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; +import { getOption } from '@utils/options.js'; import { - isRemotePath, - parseRemotePath, + globToRegex, isPathFolder, + isRemotePath, listAllItems, - globToRegex, + parseRemotePath, wildcardPrefix, -} from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig } from '../auth/s3-client.js'; -import { formatSize } from '../utils/format.js'; -import { requireInteractive, confirm } from '../utils/interactive.js'; -import { get, put, remove, list, head } from '@tigrisdata/storage'; -import { calculateUploadParams } from '../utils/upload.js'; -import { exitWithError } from '../utils/exit.js'; +} from '@utils/path.js'; +import { calculateUploadParams } from '@utils/upload.js'; let _jsonMode = false; diff --git a/src/lib/objects/delete.ts b/src/lib/objects/delete.ts index 53e0b14..1a412f3 100644 --- a/src/lib/objects/delete.ts +++ b/src/lib/objects/delete.ts @@ -1,18 +1,18 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { remove } from '@tigrisdata/storage'; -import { - printStart, - printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; import { exitWithError, getSuccessNextActions, printNextActions, -} from '../../utils/exit.js'; -import { requireInteractive, confirm } from '../../utils/interactive.js'; +} from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; +import { + msg, + printFailure, + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('objects', 'delete'); diff --git a/src/lib/objects/get.ts b/src/lib/objects/get.ts index 1f06fab..dc855b4 100644 --- a/src/lib/objects/get.ts +++ b/src/lib/objects/get.ts @@ -1,17 +1,17 @@ -import { createWriteStream, writeFileSync } from 'fs'; -import { Readable } from 'stream'; -import { pipeline } from 'stream/promises'; -import { extname } from 'path'; -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { get } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; +import { createWriteStream, writeFileSync } from 'fs'; +import { extname } from 'path'; +import { Readable } from 'stream'; +import { pipeline } from 'stream/promises'; const context = msg('objects', 'get'); diff --git a/src/lib/objects/list.ts b/src/lib/objects/list.ts index 703d574..c3e866d 100644 --- a/src/lib/objects/list.ts +++ b/src/lib/objects/list.ts @@ -1,15 +1,15 @@ -import { getOption } from '../../utils/options.js'; -import { formatOutput, formatSize } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { list } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput, formatSize } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('objects', 'list'); diff --git a/src/lib/objects/put.ts b/src/lib/objects/put.ts index 8c6d2e2..4bbe5b2 100644 --- a/src/lib/objects/put.ts +++ b/src/lib/objects/put.ts @@ -1,17 +1,17 @@ -import { createReadStream, statSync } from 'fs'; -import { Readable } from 'stream'; -import { getOption } from '../../utils/options.js'; -import { formatOutput, formatSize } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { put } from '@tigrisdata/storage'; +import { exitWithError, printNextActions } from '@utils/exit.js'; +import { formatOutput, formatSize } from '@utils/format.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; -import { calculateUploadParams } from '../../utils/upload.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; +import { calculateUploadParams } from '@utils/upload.js'; +import { createReadStream, statSync } from 'fs'; +import { Readable } from 'stream'; const context = msg('objects', 'put'); diff --git a/src/lib/objects/set.ts b/src/lib/objects/set.ts index 6eb096c..293563a 100644 --- a/src/lib/objects/set.ts +++ b/src/lib/objects/set.ts @@ -1,13 +1,13 @@ -import { getStorageConfig } from '../../auth/s3-client.js'; -import { getOption } from '../../utils/options.js'; +import { getStorageConfig } from '@auth/provider.js'; +import { updateObject } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; -import { updateObject } from '@tigrisdata/storage'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('objects', 'set'); diff --git a/src/lib/organizations/create.ts b/src/lib/organizations/create.ts index 2b4f4a4..25ac84e 100644 --- a/src/lib/organizations/create.ts +++ b/src/lib/organizations/create.ts @@ -1,20 +1,20 @@ -import { createOrganization } from '@tigrisdata/iam'; -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { isFlyUser } from '@auth/fly.js'; +import { getStorageConfig } from '@auth/provider.js'; import { - getLoginMethod, getCredentials, + getLoginMethod, getSelectedOrganization, -} from '../../auth/storage.js'; -import { isFlyUser } from '../../auth/fly.js'; +} from '@auth/storage.js'; +import { createOrganization } from '@tigrisdata/iam'; +import { exitWithError, printNextActions } from '@utils/exit.js'; import { - printStart, - printSuccess, + msg, printFailure, printHint, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; + printStart, + printSuccess, +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('organizations', 'create'); diff --git a/src/lib/organizations/list.ts b/src/lib/organizations/list.ts index 0a7b912..e3ea7a6 100644 --- a/src/lib/organizations/list.ts +++ b/src/lib/organizations/list.ts @@ -1,25 +1,25 @@ -import { listOrganizations } from '@tigrisdata/iam'; -import { getOption } from '../../utils/options.js'; -import { formatOutput } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getAuthClient } from '@auth/client.js'; +import { fetchOrganizationsFromUserInfo, isFlyUser } from '@auth/fly.js'; +import { getStorageConfig } from '@auth/provider.js'; import { - storeSelectedOrganization, - getSelectedOrganization, - getLoginMethod, getCredentials, -} from '../../auth/storage.js'; -import { getAuthClient } from '../../auth/client.js'; -import { isFlyUser, fetchOrganizationsFromUserInfo } from '../../auth/fly.js'; -import Enquirer from 'enquirer'; -import { requireInteractive } from '../../utils/interactive.js'; + getLoginMethod, + getSelectedOrganization, + storeSelectedOrganization, +} from '@auth/storage.js'; +import { listOrganizations } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; +import { requireInteractive } from '@utils/interactive.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; +import Enquirer from 'enquirer'; const context = msg('organizations', 'list'); diff --git a/src/lib/organizations/select.ts b/src/lib/organizations/select.ts index 8696bcf..98c3c73 100644 --- a/src/lib/organizations/select.ts +++ b/src/lib/organizations/select.ts @@ -1,18 +1,18 @@ -import { listOrganizations } from '@tigrisdata/iam'; -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { - storeSelectedOrganization, - getLoginMethod, getCredentials, -} from '../../auth/storage.js'; + getLoginMethod, + storeSelectedOrganization, +} from '@auth/storage.js'; +import { listOrganizations } from '@tigrisdata/iam'; +import { exitWithError, printNextActions } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError, printNextActions } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('organizations', 'select'); diff --git a/src/lib/presign.ts b/src/lib/presign.ts index ec33c5c..2dfa0ac 100644 --- a/src/lib/presign.ts +++ b/src/lib/presign.ts @@ -1,14 +1,14 @@ -import { parseAnyPath } from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig, getLoginMethod } from '../auth/s3-client.js'; -import { getPresignedUrl } from '@tigrisdata/storage'; -import { listAccessKeys } from '@tigrisdata/iam'; +import { getAuthClient } from '@auth/client.js'; +import { getLoginMethod, getStorageConfig } from '@auth/provider.js'; +import { getTigrisConfig } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import type { AccessKey } from '@tigrisdata/iam'; -import { getAuthClient } from '../auth/client.js'; -import { getSelectedOrganization } from '../auth/storage.js'; -import { getTigrisConfig } from '../auth/config.js'; -import { formatJson } from '../utils/format.js'; -import { exitWithError } from '../utils/exit.js'; +import { listAccessKeys } from '@tigrisdata/iam'; +import { getPresignedUrl } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatJson } from '@utils/format.js'; +import { getOption } from '@utils/options.js'; +import { parseAnyPath } from '@utils/path.js'; import enquirer from 'enquirer'; const { prompt } = enquirer; diff --git a/src/lib/rm.ts b/src/lib/rm.ts index 7a14057..03a0b74 100644 --- a/src/lib/rm.ts +++ b/src/lib/rm.ts @@ -1,16 +1,16 @@ +import { getStorageConfig } from '@auth/provider.js'; +import { list, remove, removeBucket } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { confirm, requireInteractive } from '@utils/interactive.js'; +import { getOption } from '@utils/options.js'; import { - isRemotePath, - parseRemotePath, + globToRegex, isPathFolder, + isRemotePath, listAllItems, - globToRegex, + parseRemotePath, wildcardPrefix, -} from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig } from '../auth/s3-client.js'; -import { remove, removeBucket, list } from '@tigrisdata/storage'; -import { requireInteractive, confirm } from '../utils/interactive.js'; -import { exitWithError } from '../utils/exit.js'; +} from '@utils/path.js'; let _jsonMode = false; diff --git a/src/lib/snapshots/list.ts b/src/lib/snapshots/list.ts index 8920e4b..d814f98 100644 --- a/src/lib/snapshots/list.ts +++ b/src/lib/snapshots/list.ts @@ -1,15 +1,15 @@ -import { getOption } from '../../utils/options.js'; -import { formatOutput } from '../../utils/format.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { listBucketSnapshots } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput } from '@utils/format.js'; import { + msg, + printEmpty, + printFailure, printStart, printSuccess, - printFailure, - printEmpty, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('snapshots', 'list'); diff --git a/src/lib/snapshots/take.ts b/src/lib/snapshots/take.ts index fd9638f..6cc2648 100644 --- a/src/lib/snapshots/take.ts +++ b/src/lib/snapshots/take.ts @@ -1,13 +1,13 @@ -import { getOption } from '../../utils/options.js'; -import { getStorageConfig } from '../../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { createBucketSnapshot } from '@tigrisdata/storage'; +import { exitWithError } from '@utils/exit.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../../utils/messages.js'; -import { exitWithError } from '../../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('snapshots', 'take'); diff --git a/src/lib/stat.ts b/src/lib/stat.ts index a112044..dfab326 100644 --- a/src/lib/stat.ts +++ b/src/lib/stat.ts @@ -1,16 +1,16 @@ -import { parseAnyPath } from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { formatOutput, formatSize } from '../utils/format.js'; -import { getStorageConfig } from '../auth/s3-client.js'; -import { getStats, getBucketInfo, head } from '@tigrisdata/storage'; +import { getStorageConfig } from '@auth/provider.js'; +import { getBucketInfo, getStats, head } from '@tigrisdata/storage'; +import { buildBucketInfo } from '@utils/bucket-info.js'; +import { exitWithError } from '@utils/exit.js'; +import { formatOutput, formatSize } from '@utils/format.js'; import { + msg, + printFailure, printStart, printSuccess, - printFailure, - msg, -} from '../utils/messages.js'; -import { buildBucketInfo } from '../utils/bucket-info.js'; -import { exitWithError } from '../utils/exit.js'; +} from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; +import { parseAnyPath } from '@utils/path.js'; const context = msg('stat'); diff --git a/src/lib/touch.ts b/src/lib/touch.ts index d3c3353..eb8f557 100644 --- a/src/lib/touch.ts +++ b/src/lib/touch.ts @@ -1,8 +1,8 @@ -import { parseAnyPath } from '../utils/path.js'; -import { getOption } from '../utils/options.js'; -import { getStorageConfig } from '../auth/s3-client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { put } from '@tigrisdata/storage'; -import { exitWithError } from '../utils/exit.js'; +import { exitWithError } from '@utils/exit.js'; +import { getOption } from '@utils/options.js'; +import { parseAnyPath } from '@utils/path.js'; export default async function touch(options: Record) { const pathString = getOption(options, ['path']); diff --git a/src/lib/whoami.ts b/src/lib/whoami.ts index d368f93..4592672 100644 --- a/src/lib/whoami.ts +++ b/src/lib/whoami.ts @@ -1,14 +1,14 @@ -import { listOrganizations } from '@tigrisdata/iam'; -import { getAuthClient } from '../auth/client.js'; +import { getAuthClient } from '@auth/client.js'; +import { getStorageConfig } from '@auth/provider.js'; import { - getSelectedOrganization, - getLoginMethod, getCredentials, -} from '../auth/storage.js'; -import { getStorageConfig } from '../auth/s3-client.js'; -import { printFailure, printAlreadyDone, msg } from '../utils/messages.js'; -import { exitWithError } from '../utils/exit.js'; -import { getOption } from '../utils/options.js'; + getLoginMethod, + getSelectedOrganization, +} from '@auth/storage.js'; +import { listOrganizations } from '@tigrisdata/iam'; +import { exitWithError } from '@utils/exit.js'; +import { msg, printAlreadyDone, printFailure } from '@utils/messages.js'; +import { getOption } from '@utils/options.js'; const context = msg('whoami'); diff --git a/src/specs-embedded.ts b/src/specs-embedded.ts index f10807b..1b2db67 100644 --- a/src/specs-embedded.ts +++ b/src/specs-embedded.ts @@ -1,8 +1,9 @@ // Embedded specs for binary builds — avoids readFileSync at runtime. // Only exports loadSpecs(). Helper functions are in utils/specs.ts. -import specsYaml from './specs.yaml' with { type: 'text' }; import * as YAML from 'yaml'; + +import specsYaml from './specs.yaml' with { type: 'text' }; import type { Specs } from './types.js'; let cachedSpecs: Specs | null = null; diff --git a/src/utils/bucket-info.ts b/src/utils/bucket-info.ts index 7bd898e..4c28dc2 100644 --- a/src/utils/bucket-info.ts +++ b/src/utils/bucket-info.ts @@ -1,4 +1,5 @@ import type { BucketInfoResponse } from '@tigrisdata/storage'; + import { formatSize } from './format.js'; export function buildBucketInfo(data: BucketInfoResponse) { diff --git a/src/utils/exit.ts b/src/utils/exit.ts index b846977..9cae058 100644 --- a/src/utils/exit.ts +++ b/src/utils/exit.ts @@ -1,8 +1,8 @@ -import { classifyError } from './errors.js'; import type { NextAction } from '../types.js'; -import { getCommandSpec } from './specs.js'; -import { interpolate } from './messages.js'; +import { classifyError } from './errors.js'; import type { MessageContext, MessageVariables } from './messages.js'; +import { interpolate } from './messages.js'; +import { getCommandSpec } from './specs.js'; function isJsonMode(): boolean { return globalThis.__TIGRIS_JSON_MODE === true; diff --git a/src/utils/locations.ts b/src/utils/locations.ts index 5e502ad..e3c48e8 100644 --- a/src/utils/locations.ts +++ b/src/utils/locations.ts @@ -1,5 +1,6 @@ import type { BucketLocations } from '@tigrisdata/storage'; import enquirer from 'enquirer'; + import { requireInteractive } from './interactive.js'; const { prompt } = enquirer; diff --git a/src/utils/messages.ts b/src/utils/messages.ts index 24deec4..82b9266 100644 --- a/src/utils/messages.ts +++ b/src/utils/messages.ts @@ -1,5 +1,5 @@ +import type { CommandSpec, Messages, OperationSpec } from '../types.js'; import { getCommandSpec } from './specs.js'; -import type { CommandSpec, OperationSpec, Messages } from '../types.js'; export type MessageVariables = Record< string, diff --git a/src/utils/path.ts b/src/utils/path.ts index f93a990..e2eb50a 100644 --- a/src/utils/path.ts +++ b/src/utils/path.ts @@ -1,6 +1,7 @@ +import type { TigrisStorageConfig } from '@auth/provider.js'; import { list } from '@tigrisdata/storage'; + import type { ParsedPath, ParsedPaths } from '../types.js'; -import type { TigrisStorageConfig } from '../auth/s3-client.js'; const REMOTE_PREFIXES = ['t3://', 'tigris://']; diff --git a/src/utils/specs.ts b/src/utils/specs.ts index 0c3b4a5..3bd33fa 100644 --- a/src/utils/specs.ts +++ b/src/utils/specs.ts @@ -1,8 +1,9 @@ import { readFileSync } from 'fs'; -import { join, dirname } from 'path'; +import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; import * as YAML from 'yaml'; -import type { Specs, CommandSpec, OperationSpec, Argument } from '../types.js'; + +import type { Argument, CommandSpec, OperationSpec, Specs } from '../types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); diff --git a/src/utils/update-check.ts b/src/utils/update-check.ts index 4cb6143..d11d6e0 100644 --- a/src/utils/update-check.ts +++ b/src/utils/update-check.ts @@ -1,7 +1,8 @@ -import { readFileSync, writeFileSync, mkdirSync } from 'fs'; -import { join } from 'path'; -import { homedir } from 'os'; +import { mkdirSync, readFileSync, writeFileSync } from 'fs'; import https from 'https'; +import { homedir } from 'os'; +import { join } from 'path'; + import { version as currentVersion } from '../../package.json'; import { NPM_REGISTRY_URL, diff --git a/test/auth/storage.test.ts b/test/auth/storage.test.ts new file mode 100644 index 0000000..a02586a --- /dev/null +++ b/test/auth/storage.test.ts @@ -0,0 +1,413 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { + mkdtempSync, + mkdirSync, + writeFileSync, + readFileSync, + rmSync, +} from 'fs'; +import { join } from 'path'; +import { tmpdir } from 'os'; + +// Mock os.homedir() to return a temp directory so tests don't touch real config +let tempHome: string; +vi.mock('os', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + homedir: () => tempHome, + }; +}); + +function configPath(): string { + return join(tempHome, '.tigris', 'config.json'); +} + +function writeRawConfig(data: unknown): void { + mkdirSync(join(tempHome, '.tigris'), { recursive: true }); + writeFileSync(configPath(), JSON.stringify(data, null, 2)); +} + +function readRawConfig(): unknown { + return JSON.parse(readFileSync(configPath(), 'utf8')); +} + +describe('auth/storage', () => { + beforeEach(() => { + tempHome = mkdtempSync(join(tmpdir(), 'tigris-test-')); + // Reset module cache so each test gets fresh state + vi.resetModules(); + }); + + afterEach(() => { + rmSync(tempHome, { recursive: true, force: true }); + }); + + // --------------------------------------------------------------------------- + // v1 → v2 Migration + // --------------------------------------------------------------------------- + describe('v1 → v2 migration', () => { + it('preserves saved credentials', async () => { + const v1Config = { + tokens: { + accessToken: 'old-token', + refreshToken: 'old-refresh', + expiresAt: 999, + }, + organizations: [{ id: 'org-1', name: 'My Org' }], + selectedOrganization: 'org-1', + credentials: { + accessKeyId: 'AKID', + secretAccessKey: 'SECRET', + endpoint: 'https://example.com', + }, + temporaryCredentials: { + accessKeyId: 'TMP-AKID', + secretAccessKey: 'TMP-SECRET', + endpoint: 'https://example.com', + }, + loginMethod: 'oauth', + }; + writeRawConfig(v1Config); + + const storage = await import('../../src/auth/storage.js'); + const creds = storage.getStoredCredentials(); + + expect(creds).toEqual({ + accessKeyId: 'AKID', + secretAccessKey: 'SECRET', + endpoint: 'https://example.com', + }); + }); + + it('discards tokens, orgs, loginMethod, temporaryCredentials', async () => { + const v1Config = { + tokens: { accessToken: 't', expiresAt: 1 }, + organizations: [{ id: 'org-1', name: 'Org' }], + selectedOrganization: 'org-1', + credentials: { + accessKeyId: 'AKID', + secretAccessKey: 'SECRET', + endpoint: 'https://example.com', + }, + temporaryCredentials: { + accessKeyId: 'TMP', + secretAccessKey: 'TMP-S', + endpoint: 'https://tmp.com', + }, + loginMethod: 'oauth', + }; + writeRawConfig(v1Config); + + const storage = await import('../../src/auth/storage.js'); + + // Tokens should be gone + expect(await storage.getTokens()).toBeNull(); + // Organizations should be empty + expect(storage.getOrganizations()).toEqual([]); + // Login method should be cleared + expect(storage.getLoginMethod()).toBeNull(); + // selectedOrganization should be cleared + expect(storage.getSelectedOrganization()).toBeNull(); + + // Config should now be v2 on disk + const raw = readRawConfig() as Record; + expect(raw['version']).toBe(2); + }); + + it('handles v1 config with no credentials gracefully', async () => { + const v1Config = { + tokens: { accessToken: 't', expiresAt: 1 }, + loginMethod: 'oauth', + }; + writeRawConfig(v1Config); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getStoredCredentials()).toBeNull(); + expect(storage.getLoginMethod()).toBeNull(); + }); + + it('handles v1 config with invalid credentials shape', async () => { + const v1Config = { + credentials: { accessKeyId: 'AKID' }, // missing secretAccessKey and endpoint + loginMethod: 'credentials', + }; + writeRawConfig(v1Config); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getStoredCredentials()).toBeNull(); + }); + }); + + // --------------------------------------------------------------------------- + // getSelectedOrganization — method-aware branching + // --------------------------------------------------------------------------- + describe('getSelectedOrganization', () => { + it('returns OAuth org when activeMethod is oauth', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'oauth', + oauth: { selectedOrganization: 'oauth-org-1' }, + credentials: { + saved: { + accessKeyId: 'A', + secretAccessKey: 'S', + endpoint: 'https://e.com', + organizationId: 'cred-org-1', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getSelectedOrganization()).toBe('oauth-org-1'); + }); + + it('returns credential org when activeMethod is credentials', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'credentials', + oauth: { selectedOrganization: 'oauth-org-1' }, + credentials: { + saved: { + accessKeyId: 'A', + secretAccessKey: 'S', + endpoint: 'https://e.com', + organizationId: 'cred-org-1', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getSelectedOrganization()).toBe('cred-org-1'); + }); + + it('prefers temporary credential org over saved when activeMethod is credentials', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'credentials', + credentials: { + saved: { + accessKeyId: 'A', + secretAccessKey: 'S', + endpoint: 'https://e.com', + organizationId: 'saved-org', + }, + temporary: { + accessKeyId: 'T', + secretAccessKey: 'TS', + endpoint: 'https://t.com', + organizationId: 'temp-org', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getSelectedOrganization()).toBe('temp-org'); + }); + + it('returns null when no method is active', async () => { + writeRawConfig({ version: 2 }); + const storage = await import('../../src/auth/storage.js'); + expect(storage.getSelectedOrganization()).toBeNull(); + }); + }); + + // --------------------------------------------------------------------------- + // getCredentials priority: env > temporary > saved + // --------------------------------------------------------------------------- + describe('getCredentials', () => { + it('returns temporary over saved', async () => { + writeRawConfig({ + version: 2, + credentials: { + saved: { + accessKeyId: 'SAVED', + secretAccessKey: 'S', + endpoint: 'https://s.com', + }, + temporary: { + accessKeyId: 'TEMP', + secretAccessKey: 'T', + endpoint: 'https://t.com', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getCredentials()?.accessKeyId).toBe('TEMP'); + }); + + it('falls back to saved when no temporary', async () => { + writeRawConfig({ + version: 2, + credentials: { + saved: { + accessKeyId: 'SAVED', + secretAccessKey: 'S', + endpoint: 'https://s.com', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getCredentials()?.accessKeyId).toBe('SAVED'); + }); + + it('returns null when no credentials exist', async () => { + writeRawConfig({ version: 2 }); + const storage = await import('../../src/auth/storage.js'); + expect(storage.getCredentials()).toBeNull(); + }); + }); + + // --------------------------------------------------------------------------- + // clearAllData preserves credentials.saved + // --------------------------------------------------------------------------- + describe('clearAllData', () => { + it('preserves saved credentials', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'credentials', + oauth: { + tokens: { accessToken: 't', expiresAt: 1 }, + organizations: [{ id: 'org-1', name: 'Org' }], + selectedOrganization: 'org-1', + }, + credentials: { + saved: { + accessKeyId: 'AKID', + secretAccessKey: 'SECRET', + endpoint: 'https://e.com', + organizationId: 'org-1', + }, + temporary: { + accessKeyId: 'TMP', + secretAccessKey: 'TMP-S', + endpoint: 'https://t.com', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + await storage.clearAllData(); + + // Saved creds should survive + const raw = readRawConfig() as Record; + const creds = raw['credentials'] as Record; + expect(creds['saved']).toBeDefined(); + const saved = creds['saved'] as Record; + expect(saved['accessKeyId']).toBe('AKID'); + + // Temporary should be gone + expect(creds['temporary']).toBeUndefined(); + + // OAuth should be gone + expect(raw['oauth']).toBeUndefined(); + + // activeMethod should be gone + expect(raw['activeMethod']).toBeUndefined(); + }); + + it('works when no saved credentials exist', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'oauth', + oauth: { + tokens: { accessToken: 't', expiresAt: 1 }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + await storage.clearAllData(); + + const raw = readRawConfig() as Record; + expect(raw['version']).toBe(2); + expect(raw['oauth']).toBeUndefined(); + expect(raw['credentials']).toBeUndefined(); + }); + }); + + // --------------------------------------------------------------------------- + // storeCredentialOrganization + // --------------------------------------------------------------------------- + describe('storeCredentialOrganization', () => { + it('writes to temporary slot when it exists', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'credentials', + credentials: { + saved: { + accessKeyId: 'S-AK', + secretAccessKey: 'S-SK', + endpoint: 'https://s.com', + }, + temporary: { + accessKeyId: 'T-AK', + secretAccessKey: 'T-SK', + endpoint: 'https://t.com', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + await storage.storeCredentialOrganization('my-org'); + + const raw = readRawConfig() as Record; + const creds = raw['credentials'] as Record; + const temp = creds['temporary'] as Record; + const saved = creds['saved'] as Record; + + expect(temp['organizationId']).toBe('my-org'); + // Saved should NOT have been modified + expect(saved['organizationId']).toBeUndefined(); + }); + + it('writes to saved slot when no temporary exists', async () => { + writeRawConfig({ + version: 2, + activeMethod: 'credentials', + credentials: { + saved: { + accessKeyId: 'S-AK', + secretAccessKey: 'S-SK', + endpoint: 'https://s.com', + }, + }, + }); + + const storage = await import('../../src/auth/storage.js'); + await storage.storeCredentialOrganization('saved-org'); + + const raw = readRawConfig() as Record; + const creds = raw['credentials'] as Record; + const saved = creds['saved'] as Record; + + expect(saved['organizationId']).toBe('saved-org'); + }); + + it('does nothing when no credential slots exist', async () => { + writeRawConfig({ version: 2 }); + + const storage = await import('../../src/auth/storage.js'); + await storage.storeCredentialOrganization('org-id'); + + const raw = readRawConfig() as Record; + expect(raw['credentials']).toBeUndefined(); + }); + }); + + // --------------------------------------------------------------------------- + // storeLoginMethod / getLoginMethod + // --------------------------------------------------------------------------- + describe('storeLoginMethod / getLoginMethod', () => { + it('stores and retrieves login method', async () => { + writeRawConfig({ version: 2 }); + + const storage = await import('../../src/auth/storage.js'); + expect(storage.getLoginMethod()).toBeNull(); + + await storage.storeLoginMethod('oauth'); + expect(storage.getLoginMethod()).toBe('oauth'); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 208cdd0..00f9262 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,11 @@ "strict": true, "noEmit": true, "resolveJsonModule": true, - "moduleDetection": "force" + "moduleDetection": "force", + "paths": { + "@auth/*": ["./src/auth/*"], + "@utils/*": ["./src/utils/*"] + } }, "include": ["src"], "exclude": ["src/cli-binary.ts", "src/command-registry.ts"] diff --git a/tsup.config.ts b/tsup.config.ts index bb3109b..eb66747 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'tsup'; import { copyFileSync, watch } from 'fs'; import { join } from 'path'; +import { defineConfig } from 'tsup'; const copySpecs = () => { copyFileSync( @@ -10,6 +10,12 @@ const copySpecs = () => { }; export default defineConfig((options) => ({ + esbuildOptions(options) { + options.alias = { + '@auth': './src/auth', + '@utils': './src/utils', + }; + }, entry: [ 'src/cli.ts', 'src/**/*.ts', diff --git a/vitest.config.ts b/vitest.config.ts index b0f997a..017d465 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,6 +6,12 @@ import dotenv from 'dotenv'; dotenv.config({ path: path.resolve(__dirname, '.env') }); export default defineConfig({ + resolve: { + alias: { + '@auth': path.resolve(__dirname, 'src/auth'), + '@utils': path.resolve(__dirname, 'src/utils'), + }, + }, test: { globals: true, environment: 'node', From 7c0f0d2b8fcbb90e325c396db36f2c63bf54ce21 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Sun, 29 Mar 2026 11:20:34 +0200 Subject: [PATCH 3/5] refactor: extract shared auth and error-handling helpers Consolidate ~250 lines of duplicated boilerplate across 35+ command files into 4 shared helpers: failWithError, getOAuthIAMConfig, getStorageConfigWithOrg, and requireOAuthLogin. Delete access-keys/config.ts (moved to auth/iam.ts). Co-Authored-By: Claude Opus 4.6 --- src/auth/iam.ts | 102 ++++++++++++++++++++ src/auth/provider.ts | 37 ++++++++ src/lib/access-keys/assign.ts | 84 +++-------------- src/lib/access-keys/create.ts | 56 ++--------- src/lib/access-keys/delete.ts | 56 ++--------- src/lib/access-keys/get.ts | 56 ++--------- src/lib/access-keys/list.ts | 54 ++--------- src/lib/buckets/create.ts | 21 ++--- src/lib/buckets/delete.ts | 4 +- src/lib/buckets/get.ts | 15 +-- src/lib/buckets/list.ts | 123 +++++++++++-------------- src/lib/buckets/set-cors.ts | 36 ++------ src/lib/buckets/set-locations.ts | 30 ++---- src/lib/buckets/set-migration.ts | 39 ++------ src/lib/buckets/set-notifications.ts | 48 +++------- src/lib/buckets/set-transition.ts | 60 +++--------- src/lib/buckets/set-ttl.ts | 45 +++------ src/lib/buckets/set.ts | 32 ++----- src/lib/configure/index.ts | 5 +- src/lib/credentials/test.ts | 8 +- src/lib/forks/create.ts | 18 +--- src/lib/forks/list.ts | 19 +--- src/lib/iam/policies/create.ts | 69 ++------------ src/lib/iam/policies/delete.ts | 55 ++--------- src/lib/iam/policies/edit.ts | 73 +++------------ src/lib/iam/policies/get.ts | 55 ++--------- src/lib/iam/policies/list.ts | 52 ++--------- src/lib/iam/users/invite.ts | 72 ++------------- src/lib/iam/users/list.ts | 64 ++----------- src/lib/iam/users/remove.ts | 67 ++------------ src/lib/iam/users/revoke-invitation.ts | 67 ++------------ src/lib/iam/users/update-role.ts | 85 ++--------------- src/lib/login/credentials.ts | 12 +-- src/lib/logout.ts | 16 +--- src/lib/objects/delete.ts | 7 +- src/lib/objects/get.ts | 21 ++--- src/lib/objects/list.ts | 16 +--- src/lib/objects/put.ts | 24 ++--- src/lib/objects/set.ts | 25 ++--- src/lib/organizations/create.ts | 42 ++------- src/lib/organizations/list.ts | 35 +------ src/lib/organizations/select.ts | 34 ++----- src/lib/snapshots/list.ts | 16 +--- src/lib/snapshots/take.ts | 15 +-- src/lib/stat.ts | 24 ++--- src/lib/whoami.ts | 14 +-- src/utils/exit.ts | 11 ++- 47 files changed, 485 insertions(+), 1434 deletions(-) create mode 100644 src/auth/iam.ts diff --git a/src/auth/iam.ts b/src/auth/iam.ts new file mode 100644 index 0000000..2e76644 --- /dev/null +++ b/src/auth/iam.ts @@ -0,0 +1,102 @@ +/** + * Shared IAM auth helpers + * Consolidates OAuth check + auth check + config building patterns + */ + +import { failWithError } from '@utils/exit.js'; +import type { MessageContext } from '@utils/messages.js'; + +import { getAuthClient } from './client.js'; +import { isFlyUser } from './fly.js'; +import { getLoginMethod, getTigrisConfig } from './provider.js'; +import { getCredentials, getSelectedOrganization } from './storage.js'; + +/** + * Check if current org is Fly.io. Prints message and returns true if so. + */ +export function isFlyOrganization(): boolean { + const selectedOrg = getSelectedOrganization(); + if (isFlyUser(selectedOrg ?? undefined)) { + console.log( + 'User management is not available for Fly.io organizations.\n' + + 'Your users are managed through Fly.io.\n\n' + + 'Visit https://fly.io to manage your organization members.' + ); + return true; + } + return false; +} + +/** + * OAuth-only IAM config. Exits on non-OAuth or unauthenticated. + * Used by IAM policy and user commands. + */ +export async function getOAuthIAMConfig(context: MessageContext) { + const loginMethod = await getLoginMethod(); + if (loginMethod !== 'oauth') { + failWithError( + context, + 'This operation requires OAuth login.\nRun "tigris login oauth" first.' + ); + } + + const authClient = getAuthClient(); + if (!(await authClient.isAuthenticated())) { + failWithError( + context, + 'Not authenticated. Run "tigris login oauth" first.' + ); + } + + const accessToken = await authClient.getAccessToken(); + const selectedOrg = getSelectedOrganization(); + const { iamEndpoint, mgmtEndpoint } = getTigrisConfig(); + + return { + sessionToken: accessToken, + organizationId: selectedOrg ?? undefined, + iamEndpoint, + mgmtEndpoint, + }; +} + +/** + * Dual-mode IAM config (OAuth or credentials). + * Used by access-key commands. + */ +export async function getIAMConfig(context: MessageContext) { + const loginMethod = await getLoginMethod(); + const tigrisConfig = getTigrisConfig(); + const selectedOrg = getSelectedOrganization(); + + if (loginMethod === 'oauth') { + const authClient = getAuthClient(); + if (!(await authClient.isAuthenticated())) { + failWithError( + context, + 'Not authenticated. Run "tigris login oauth" first.' + ); + } + + return { + sessionToken: await authClient.getAccessToken(), + organizationId: selectedOrg ?? undefined, + iamEndpoint: tigrisConfig.iamEndpoint, + }; + } + + const credentials = getCredentials(); + if (!credentials) { + failWithError( + context, + 'Not authenticated. Run "tigris login" or "tigris configure" first.' + ); + } + + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + organizationId: selectedOrg ?? undefined, + iamEndpoint: tigrisConfig.iamEndpoint, + }; +} diff --git a/src/auth/provider.ts b/src/auth/provider.ts index 649d14b..4c753fc 100644 --- a/src/auth/provider.ts +++ b/src/auth/provider.ts @@ -13,6 +13,7 @@ import { import { getAuth0Config, getAuthClient } from './client.js'; import { getAwsProfileConfig, + getCredentials, getEnvCredentials, getLoginMethod as getStoredLoginMethod, getSelectedOrganization, @@ -197,3 +198,39 @@ export async function isAuthenticated(): Promise { getStoredCredentials() !== null ); } + +/** + * Get storage config with organization overlay from selected org. + */ +export async function getStorageConfigWithOrg() { + const config = await getStorageConfig(); + const selectedOrg = getSelectedOrganization(); + return { + ...config, + ...(selectedOrg && !config.organizationId + ? { organizationId: selectedOrg } + : {}), + }; +} + +/** + * Require OAuth login for organization operations. + * Returns true if NOT authenticated via OAuth (caller should return early). + */ +export function requireOAuthLogin(operation: string): boolean { + const loginMethod = getStoredLoginMethod(); + if (loginMethod === 'oauth') return false; + + if (getCredentials()) { + console.log( + `You are using access key credentials, which belong to a single organization.\n` + + `${operation} is only available with OAuth login.\n\n` + + `Run "tigris login" to login with your Tigris account.` + ); + } else { + console.log( + 'Not authenticated. Please run "tigris login" to login with your Tigris account.' + ); + } + return true; +} diff --git a/src/lib/access-keys/assign.ts b/src/lib/access-keys/assign.ts index b171a76..da9f671 100644 --- a/src/lib/access-keys/assign.ts +++ b/src/lib/access-keys/assign.ts @@ -1,21 +1,13 @@ +import { getIAMConfig } from '@auth/iam.js'; import { assignBucketRoles, revokeAllBucketRoles } from '@tigrisdata/iam'; import { - exitWithError, + failWithError, getSuccessNextActions, printNextActions, } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; - const context = msg('access-keys', 'assign'); type Role = 'Editor' | 'ReadOnly' | 'NamespaceAdmin'; @@ -48,55 +40,20 @@ export default async function assign(options: Record) { ); if (!id) { - printFailure(context, 'Access key ID is required'); - exitWithError('Access key ID is required', context); + failWithError(context, 'Access key ID is required'); } if (admin && revokeRoles) { - printFailure(context, 'Cannot use --admin and --revoke-roles together'); - exitWithError('Cannot use --admin and --revoke-roles together', context); - } - - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Bucket roles can only be managed when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Bucket roles can only be managed when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); + failWithError(context, 'Cannot use --admin and --revoke-roles together'); } - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); - - const config = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }; + const config = await getIAMConfig(context); if (revokeRoles) { const { error } = await revokeAllBucketRoles(id, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { @@ -114,38 +71,26 @@ export default async function assign(options: Record) { assignments = [{ bucket: '*', role: 'NamespaceAdmin' }]; } else { if (buckets.length === 0) { - printFailure( + failWithError( context, 'At least one bucket name is required (or use --admin or --revoke-roles)' ); - exitWithError( - 'At least one bucket name is required (or use --admin or --revoke-roles)', - context - ); } if (roles.length === 0) { - printFailure( + failWithError( context, 'At least one role is required (or use --admin or --revoke-roles)' ); - exitWithError( - 'At least one role is required (or use --admin or --revoke-roles)', - context - ); } // Validate all roles for (const role of roles) { if (!validRoles.includes(role as Role)) { - printFailure( + failWithError( context, `Invalid role "${role}". Valid roles are: ${validRoles.join(', ')}` ); - exitWithError( - `Invalid role "${role}". Valid roles are: ${validRoles.join(', ')}`, - context - ); } } @@ -163,22 +108,17 @@ export default async function assign(options: Record) { role: roles[i] as Role, })); } else { - printFailure( + failWithError( context, `Number of roles (${roles.length}) must be 1 or match number of buckets (${buckets.length})` ); - exitWithError( - `Number of roles (${roles.length}) must be 1 or match number of buckets (${buckets.length})`, - context - ); } } const { error } = await assignBucketRoles(id, assignments, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/access-keys/create.ts b/src/lib/access-keys/create.ts index 54442e1..fb9e87e 100644 --- a/src/lib/access-keys/create.ts +++ b/src/lib/access-keys/create.ts @@ -1,21 +1,13 @@ +import { getIAMConfig } from '@auth/iam.js'; import { createAccessKey } from '@tigrisdata/iam'; import { - exitWithError, + failWithError, getSuccessNextActions, printNextActions, } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; - const context = msg('access-keys', 'create'); export default async function create(options: Record) { @@ -29,49 +21,15 @@ export default async function create(options: Record) { const name = getOption(options, ['name']); if (!name) { - printFailure(context, 'Access key name is required'); - exitWithError('Access key name is required', context); - } - - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Access keys can only be created when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Access keys can only be created when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); + failWithError(context, 'Access key name is required'); } - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); + const config = await getIAMConfig(context); - const { data, error } = await createAccessKey(name, { - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }, - }); + const { data, error } = await createAccessKey(name, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/access-keys/delete.ts b/src/lib/access-keys/delete.ts index 8cd763a..8b35fa7 100644 --- a/src/lib/access-keys/delete.ts +++ b/src/lib/access-keys/delete.ts @@ -1,18 +1,10 @@ +import { getIAMConfig } from '@auth/iam.js'; import { removeAccessKey } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; - const context = msg('access-keys', 'delete'); export default async function remove(options: Record) { @@ -27,32 +19,7 @@ export default async function remove(options: Record) { const force = getOption(options, ['force', 'yes', 'y']); if (!id) { - printFailure(context, 'Access key ID is required'); - exitWithError('Access key ID is required', context); - } - - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Access keys can only be deleted when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Access keys can only be deleted when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); + failWithError(context, 'Access key ID is required'); } if (!force) { @@ -64,21 +31,12 @@ export default async function remove(options: Record) { } } - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); + const config = await getIAMConfig(context); - const { error } = await removeAccessKey(id, { - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }, - }); + const { error } = await removeAccessKey(id, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/access-keys/get.ts b/src/lib/access-keys/get.ts index 57195cd..8fc9b45 100644 --- a/src/lib/access-keys/get.ts +++ b/src/lib/access-keys/get.ts @@ -1,17 +1,9 @@ +import { getIAMConfig } from '@auth/iam.js'; import { getAccessKey } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; - const context = msg('access-keys', 'get'); export default async function get(options: Record) { @@ -25,49 +17,15 @@ export default async function get(options: Record) { const id = getOption(options, ['id']); if (!id) { - printFailure(context, 'Access key ID is required'); - exitWithError('Access key ID is required', context); - } - - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Access keys can only be retrieved when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Access keys can only be retrieved when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); + failWithError(context, 'Access key ID is required'); } - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); + const config = await getIAMConfig(context); - const { data, error } = await getAccessKey(id, { - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }, - }); + const { data, error } = await getAccessKey(id, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/access-keys/list.ts b/src/lib/access-keys/list.ts index eb25399..74093ee 100644 --- a/src/lib/access-keys/list.ts +++ b/src/lib/access-keys/list.ts @@ -1,19 +1,10 @@ +import { getIAMConfig } from '@auth/iam.js'; import { listAccessKeys } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod, getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; - const context = msg('access-keys', 'list'); export default async function list(options: Record) { @@ -24,45 +15,12 @@ export default async function list(options: Record) { ? 'json' : getOption(options, ['format', 'f', 'F'], 'table'); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Access keys can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Access keys can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); + const config = await getIAMConfig(context); - const { data, error } = await listAccessKeys({ - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }, - }); + const { data, error } = await listAccessKeys({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (!data.accessKeys || data.accessKeys.length === 0) { diff --git a/src/lib/buckets/create.ts b/src/lib/buckets/create.ts index 325a73f..7b41ec2 100644 --- a/src/lib/buckets/create.ts +++ b/src/lib/buckets/create.ts @@ -2,18 +2,13 @@ import { getStorageConfig } from '@auth/provider.js'; import type { BucketLocations, StorageClass } from '@tigrisdata/storage'; import { createBucket } from '@tigrisdata/storage'; import { - exitWithError, + failWithError, getSuccessNextActions, printNextActions, } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { parseLocations, promptLocations } from '@utils/locations.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { buildPromptChoices, getArgumentSpec } from '@utils/specs.js'; import enquirer from 'enquirer'; @@ -134,19 +129,16 @@ export default async function create(options: Record) { try { parsedLocations = await promptLocations(); } catch (err) { - printFailure(context, (err as Error).message); - exitWithError(err, context); + failWithError(context, err); } } if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (sourceSnapshot && !forkOf) { - printFailure(context, '--source-snapshot requires --fork-of'); - exitWithError('--source-snapshot requires --fork-of', context); + failWithError(context, '--source-snapshot requires --fork-of'); } const { error } = await createBucket(name, { @@ -160,8 +152,7 @@ export default async function create(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/buckets/delete.ts b/src/lib/buckets/delete.ts index 69948c5..b26d08a 100644 --- a/src/lib/buckets/delete.ts +++ b/src/lib/buckets/delete.ts @@ -2,6 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { removeBucket } from '@tigrisdata/storage'; import { exitWithError, + failWithError, getSuccessNextActions, printNextActions, } from '@utils/exit.js'; @@ -28,8 +29,7 @@ export default async function deleteBucket(options: Record) { const force = getOption(options, ['force', 'yes', 'y']); if (!names) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const bucketNames = Array.isArray(names) ? names : [names]; diff --git a/src/lib/buckets/get.ts b/src/lib/buckets/get.ts index b5cd779..7292768 100644 --- a/src/lib/buckets/get.ts +++ b/src/lib/buckets/get.ts @@ -1,14 +1,9 @@ import { getStorageConfig } from '@auth/provider.js'; import { getBucketInfo } from '@tigrisdata/storage'; import { buildBucketInfo } from '@utils/bucket-info.js'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'get'); @@ -23,8 +18,7 @@ export default async function get(options: Record) { : getOption(options, ['format', 'f', 'F']) || 'table'; if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const { data, error } = await getBucketInfo(name, { @@ -32,8 +26,7 @@ export default async function get(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const info = [ diff --git a/src/lib/buckets/list.ts b/src/lib/buckets/list.ts index bf2bf6c..9ab67c1 100644 --- a/src/lib/buckets/list.ts +++ b/src/lib/buckets/list.ts @@ -1,14 +1,8 @@ import { getStorageConfig } from '@auth/provider.js'; import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'list'); @@ -16,86 +10,75 @@ const context = msg('buckets', 'list'); export default async function list(options: Record) { printStart(context); - try { - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); - const forksOf = getOption(options, ['forks-of', 'forksOf']); - const config = await getStorageConfig(); + const json = getOption(options, ['json']); + const format = json + ? 'json' + : getOption(options, ['format', 'f', 'F'], 'table'); + const forksOf = getOption(options, ['forks-of', 'forksOf']); + const config = await getStorageConfig(); - const { data, error } = await listBuckets({ config }); + const { data, error } = await listBuckets({ config }); - if (error) { - printFailure(context, error.message); - exitWithError(error, context); + if (error) { + failWithError(context, error); + } + + if (!data.buckets || data.buckets.length === 0) { + printEmpty(context); + return; + } + + if (forksOf) { + // Filter for forks of the named source bucket + const { data: bucketInfo, error: infoError } = await getBucketInfo( + forksOf, + { config } + ); + + if (infoError) { + failWithError(context, infoError); } - if (!data.buckets || data.buckets.length === 0) { + if (!bucketInfo.hasForks) { printEmpty(context); return; } - if (forksOf) { - // Filter for forks of the named source bucket - const { data: bucketInfo, error: infoError } = await getBucketInfo( - forksOf, - { config } - ); - - if (infoError) { - printFailure(context, infoError.message); - exitWithError(infoError, context); - } - - if (!bucketInfo.hasForks) { - printEmpty(context); - return; - } - - const forks: Array<{ name: string; created: Date }> = []; + const forks: Array<{ name: string; created: Date }> = []; - for (const bucket of data.buckets) { - if (bucket.name === forksOf) continue; - const { data: info } = await getBucketInfo(bucket.name, { config }); - if (info?.sourceBucketName === forksOf) { - forks.push({ name: bucket.name, created: bucket.creationDate }); - } + for (const bucket of data.buckets) { + if (bucket.name === forksOf) continue; + const { data: info } = await getBucketInfo(bucket.name, { config }); + if (info?.sourceBucketName === forksOf) { + forks.push({ name: bucket.name, created: bucket.creationDate }); } + } - if (forks.length === 0) { - printEmpty(context); - return; - } - - const output = formatOutput(forks, format!, 'forks', 'fork', [ - { key: 'name', header: 'Name' }, - { key: 'created', header: 'Created' }, - ]); - - console.log(output); - printSuccess(context, { count: forks.length }); + if (forks.length === 0) { + printEmpty(context); return; } - const buckets = data.buckets.map((bucket) => ({ - name: bucket.name, - created: bucket.creationDate, - })); - - const output = formatOutput(buckets, format!, 'buckets', 'bucket', [ + const output = formatOutput(forks, format!, 'forks', 'fork', [ { key: 'name', header: 'Name' }, { key: 'created', header: 'Created' }, ]); console.log(output); - printSuccess(context, { count: buckets.length }); - } catch (error) { - if (error instanceof Error) { - printFailure(context, error.message); - } else { - printFailure(context, 'An unknown error occurred'); - } - exitWithError(error, context); + printSuccess(context, { count: forks.length }); + return; } + + const buckets = data.buckets.map((bucket) => ({ + name: bucket.name, + created: bucket.creationDate, + })); + + const output = formatOutput(buckets, format!, 'buckets', 'bucket', [ + { key: 'name', header: 'Name' }, + { key: 'created', header: 'Created' }, + ]); + + console.log(output); + printSuccess(context, { count: buckets.length }); } diff --git a/src/lib/buckets/set-cors.ts b/src/lib/buckets/set-cors.ts index f714361..0432032 100644 --- a/src/lib/buckets/set-cors.ts +++ b/src/lib/buckets/set-cors.ts @@ -1,13 +1,7 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketCors } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-cors'); @@ -28,8 +22,7 @@ export default async function setCors(options: Record) { const reset = getOption(options, ['reset']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if ( @@ -41,28 +34,18 @@ export default async function setCors(options: Record) { maxAge !== undefined || override) ) { - printFailure(context, 'Cannot use --reset with other options'); - exitWithError('Cannot use --reset with other options', context); + failWithError(context, 'Cannot use --reset with other options'); } if (!reset && !origins) { - printFailure(context, 'Provide --origins or --reset'); - exitWithError('Provide --origins or --reset', context); + failWithError(context, 'Provide --origins or --reset'); } if (maxAge !== undefined && (isNaN(Number(maxAge)) || Number(maxAge) <= 0)) { - printFailure(context, '--max-age must be a positive number'); - exitWithError('--max-age must be a positive number', context); + failWithError(context, '--max-age must be a positive number'); } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); const { error } = await setBucketCors(name, { rules: reset @@ -81,8 +64,7 @@ export default async function setCors(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set-locations.ts b/src/lib/buckets/set-locations.ts index 782c634..c1998db 100644 --- a/src/lib/buckets/set-locations.ts +++ b/src/lib/buckets/set-locations.ts @@ -1,16 +1,10 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import type { BucketLocations } from '@tigrisdata/storage'; import { updateBucket } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { parseLocations, promptLocations } from '@utils/locations.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-locations'); @@ -22,8 +16,7 @@ export default async function setLocations(options: Record) { const locations = getOption(options, ['locations']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } let parsedLocations: BucketLocations; @@ -34,19 +27,11 @@ export default async function setLocations(options: Record) { try { parsedLocations = await promptLocations(); } catch (err) { - printFailure(context, (err as Error).message); - exitWithError(err, context); + failWithError(context, err); } } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); const { error } = await updateBucket(name, { locations: parsedLocations, @@ -54,8 +39,7 @@ export default async function setLocations(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set-migration.ts b/src/lib/buckets/set-migration.ts index c5ca522..d3aa6f3 100644 --- a/src/lib/buckets/set-migration.ts +++ b/src/lib/buckets/set-migration.ts @@ -1,13 +1,7 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketMigration } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-migration'); @@ -28,8 +22,7 @@ export default async function setMigration(options: Record) { const disable = getOption(options, ['disable']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if ( @@ -41,18 +34,10 @@ export default async function setMigration(options: Record) { secretKey !== undefined || writeThrough !== undefined) ) { - printFailure(context, 'Cannot use --disable with other migration options'); - exitWithError('Cannot use --disable with other migration options', context); + failWithError(context, 'Cannot use --disable with other migration options'); } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); if (disable) { const { error } = await setBucketMigration(name, { @@ -61,8 +46,7 @@ export default async function setMigration(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); @@ -70,14 +54,10 @@ export default async function setMigration(options: Record) { } if (!bucket || !endpoint || !region || !accessKey || !secretKey) { - printFailure( + failWithError( context, 'Required: --bucket, --endpoint, --region, --access-key, --secret-key' ); - exitWithError( - 'Required: --bucket, --endpoint, --region, --access-key, --secret-key', - context - ); } const { error } = await setBucketMigration(name, { @@ -94,8 +74,7 @@ export default async function setMigration(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set-notifications.ts b/src/lib/buckets/set-notifications.ts index 758c587..ec54e90 100644 --- a/src/lib/buckets/set-notifications.ts +++ b/src/lib/buckets/set-notifications.ts @@ -1,16 +1,10 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { type BucketNotification, setBucketNotifications, } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-notifications'); @@ -31,20 +25,15 @@ export default async function setNotifications( const reset = getOption(options, ['reset']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const flagCount = [enable, disable, reset].filter(Boolean).length; if (flagCount > 1) { - printFailure( + failWithError( context, 'Only one of --enable, --disable, or --reset can be used' ); - exitWithError( - 'Only one of --enable, --disable, or --reset can be used', - context - ); } if ( @@ -55,8 +44,7 @@ export default async function setNotifications( username !== undefined || password !== undefined) ) { - printFailure(context, 'Cannot use --reset with other options'); - exitWithError('Cannot use --reset with other options', context); + failWithError(context, 'Cannot use --reset with other options'); } if ( @@ -69,37 +57,24 @@ export default async function setNotifications( username === undefined && password === undefined ) { - printFailure(context, 'Provide at least one option'); - exitWithError('Provide at least one option', context); + failWithError(context, 'Provide at least one option'); } if (token && (username !== undefined || password !== undefined)) { - printFailure( + failWithError( context, 'Cannot use --token with --username/--password. Choose one auth method' ); - exitWithError( - 'Cannot use --token with --username/--password. Choose one auth method', - context - ); } if ( (username !== undefined && password === undefined) || (username === undefined && password !== undefined) ) { - printFailure(context, 'Both --username and --password are required'); - exitWithError('Both --username and --password are required', context); + failWithError(context, 'Both --username and --password are required'); } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); let notificationConfig: BucketNotification; @@ -129,8 +104,7 @@ export default async function setNotifications( }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set-transition.ts b/src/lib/buckets/set-transition.ts index 39213cd..581677d 100644 --- a/src/lib/buckets/set-transition.ts +++ b/src/lib/buckets/set-transition.ts @@ -1,16 +1,10 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { type BucketLifecycleRule, setBucketLifecycle, } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-transition'); @@ -31,59 +25,43 @@ export default async function setTransitions(options: Record) { const disable = getOption(options, ['disable']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (enable && disable) { - printFailure(context, 'Cannot use both --enable and --disable'); - exitWithError('Cannot use both --enable and --disable', context); + failWithError(context, 'Cannot use both --enable and --disable'); } if ( disable && (days !== undefined || date !== undefined || storageClass !== undefined) ) { - printFailure( + failWithError( context, 'Cannot use --disable with --days, --date, or --storage-class' ); - exitWithError( - 'Cannot use --disable with --days, --date, or --storage-class', - context - ); } if (!enable && !disable && days === undefined && date === undefined) { - printFailure(context, 'Provide --days, --date, --enable, or --disable'); - exitWithError('Provide --days, --date, --enable, or --disable', context); + failWithError(context, 'Provide --days, --date, --enable, or --disable'); } if ((days !== undefined || date !== undefined) && !storageClass) { - printFailure( + failWithError( context, '--storage-class is required when setting --days or --date' ); - exitWithError( - '--storage-class is required when setting --days or --date', - context - ); } if (storageClass && !VALID_TRANSITION_CLASSES.includes(storageClass)) { - printFailure( + failWithError( context, `--storage-class must be one of: ${VALID_TRANSITION_CLASSES.join(', ')} (STANDARD is not a valid transition target)` ); - exitWithError( - `--storage-class must be one of: ${VALID_TRANSITION_CLASSES.join(', ')} (STANDARD is not a valid transition target)`, - context - ); } if (days !== undefined && (isNaN(Number(days)) || Number(days) <= 0)) { - printFailure(context, '--days must be a positive number'); - exitWithError('--days must be a positive number', context); + failWithError(context, '--days must be a positive number'); } if (date !== undefined) { @@ -92,25 +70,14 @@ export default async function setTransitions(options: Record) { !/^\d{4}-\d{2}-\d{2}/.test(date) || isNaN(new Date(date).getTime()) ) { - printFailure( + failWithError( context, '--date must be a valid ISO-8601 date (e.g. 2026-06-01)' ); - exitWithError( - '--date must be a valid ISO-8601 date (e.g. 2026-06-01)', - context - ); } } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); const rule: BucketLifecycleRule = { ...(enable ? { enabled: true } : {}), @@ -128,8 +95,7 @@ export default async function setTransitions(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set-ttl.ts b/src/lib/buckets/set-ttl.ts index 5db5a95..56421e3 100644 --- a/src/lib/buckets/set-ttl.ts +++ b/src/lib/buckets/set-ttl.ts @@ -1,13 +1,7 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketTtl } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('buckets', 'set-ttl'); @@ -22,28 +16,23 @@ export default async function setTtl(options: Record) { const disable = getOption(options, ['disable']); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (enable && disable) { - printFailure(context, 'Cannot use both --enable and --disable'); - exitWithError('Cannot use both --enable and --disable', context); + failWithError(context, 'Cannot use both --enable and --disable'); } if (disable && (days !== undefined || date !== undefined)) { - printFailure(context, 'Cannot use --disable with --days or --date'); - exitWithError('Cannot use --disable with --days or --date', context); + failWithError(context, 'Cannot use --disable with --days or --date'); } if (!enable && !disable && days === undefined && date === undefined) { - printFailure(context, 'Provide --days, --date, --enable, or --disable'); - exitWithError('Provide --days, --date, --enable, or --disable', context); + failWithError(context, 'Provide --days, --date, --enable, or --disable'); } if (days !== undefined && (isNaN(Number(days)) || Number(days) <= 0)) { - printFailure(context, '--days must be a positive number'); - exitWithError('--days must be a positive number', context); + failWithError(context, '--days must be a positive number'); } if (date !== undefined) { @@ -52,25 +41,14 @@ export default async function setTtl(options: Record) { !/^\d{4}-\d{2}-\d{2}/.test(date) || isNaN(new Date(date).getTime()) ) { - printFailure( + failWithError( context, '--date must be a valid ISO-8601 date (e.g. 2026-06-01)' ); - exitWithError( - '--date must be a valid ISO-8601 date (e.g. 2026-06-01)', - context - ); } } - const config = await getStorageConfig(); - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); const ttlConfig = { ...(enable ? { enabled: true } : {}), @@ -85,8 +63,7 @@ export default async function setTtl(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name }); diff --git a/src/lib/buckets/set.ts b/src/lib/buckets/set.ts index 989dacc..4871184 100644 --- a/src/lib/buckets/set.ts +++ b/src/lib/buckets/set.ts @@ -1,14 +1,8 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getStorageConfigWithOrg } from '@auth/provider.js'; import { updateBucket, type UpdateBucketOptions } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { parseLocations } from '@utils/locations.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption, parseBoolean } from '@utils/options.js'; const context = msg('buckets', 'set'); @@ -61,8 +55,7 @@ export default async function set(options: Record) { ]); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } // Check if at least one setting is provided @@ -76,12 +69,9 @@ export default async function set(options: Record) { enableDeleteProtection === undefined && enableAdditionalHeaders === undefined ) { - printFailure(context, 'At least one setting is required'); - exitWithError('At least one setting is required', context); + failWithError(context, 'At least one setting is required'); } - const config = await getStorageConfig(); - // Build update options from provided settings const updateOptions: UpdateBucketOptions = {}; @@ -121,14 +111,7 @@ export default async function set(options: Record) { ); } - // Include organization ID if available (needed for updateBucket API) - const selectedOrg = getSelectedOrganization(); - const finalConfig = { - ...config, - ...(selectedOrg && !config.organizationId - ? { organizationId: selectedOrg } - : {}), - }; + const finalConfig = await getStorageConfigWithOrg(); const { error } = await updateBucket(name, { ...updateOptions, @@ -136,8 +119,7 @@ export default async function set(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/configure/index.ts b/src/lib/configure/index.ts index 592e82e..530db73 100644 --- a/src/lib/configure/index.ts +++ b/src/lib/configure/index.ts @@ -7,7 +7,7 @@ import { storeLoginMethod, } from '@auth/storage.js'; import { whoami } from '@tigrisdata/iam'; -import { exitWithError, printNextActions } from '@utils/exit.js'; +import { exitWithError, failWithError, printNextActions } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { msg, @@ -85,8 +85,7 @@ export default async function configure(options: Record) { // Validate that all required fields are present if (!accessKey || !accessSecret || !endpoint) { - printFailure(context, 'All credentials are required'); - exitWithError('All credentials are required', context); + failWithError(context, 'All credentials are required'); } // Store credentials diff --git a/src/lib/credentials/test.ts b/src/lib/credentials/test.ts index 70baefc..57c719a 100644 --- a/src/lib/credentials/test.ts +++ b/src/lib/credentials/test.ts @@ -1,7 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { getSelectedOrganization } from '@auth/storage.js'; import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { exitWithError, failWithError } from '@utils/exit.js'; import { msg, printFailure, @@ -25,14 +25,10 @@ export default async function test(options: Record) { const config = await getStorageConfig(); if (!config.accessKeyId && !config.sessionToken) { - printFailure( + failWithError( context, 'No credentials found. Run "tigris configure" or "tigris login" first.' ); - exitWithError( - 'No credentials found. Run "tigris configure" or "tigris login" first.', - context - ); } // Include organization ID if available diff --git a/src/lib/forks/create.ts b/src/lib/forks/create.ts index 396b51e..ef252d6 100644 --- a/src/lib/forks/create.ts +++ b/src/lib/forks/create.ts @@ -1,12 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { createBucket } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('forks', 'create'); @@ -19,13 +14,11 @@ export default async function create(options: Record) { const snapshot = getOption(options, ['snapshot', 's', 'S']); if (!name) { - printFailure(context, 'Source bucket name is required'); - exitWithError('Source bucket name is required', context); + failWithError(context, 'Source bucket name is required'); } if (!forkName) { - printFailure(context, 'Fork name is required'); - exitWithError('Fork name is required', context); + failWithError(context, 'Fork name is required'); } const { error } = await createBucket(forkName, { @@ -35,8 +28,7 @@ export default async function create(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name, forkName }); diff --git a/src/lib/forks/list.ts b/src/lib/forks/list.ts index 17a276f..0a0910c 100644 --- a/src/lib/forks/list.ts +++ b/src/lib/forks/list.ts @@ -1,14 +1,8 @@ import { getStorageConfig } from '@auth/provider.js'; import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('forks', 'list'); @@ -23,8 +17,7 @@ export default async function list(options: Record) { : getOption(options, ['format', 'f', 'F'], 'table'); if (!name) { - printFailure(context, 'Source bucket name is required'); - exitWithError('Source bucket name is required', context); + failWithError(context, 'Source bucket name is required'); } const config = await getStorageConfig(); @@ -35,8 +28,7 @@ export default async function list(options: Record) { }); if (infoError) { - printFailure(context, infoError.message); - exitWithError(infoError, context); + failWithError(context, infoError); } if (!bucketInfo.hasForks) { @@ -48,8 +40,7 @@ export default async function list(options: Record) { const { data, error } = await listBuckets({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } // Get info for each bucket to find forks diff --git a/src/lib/iam/policies/create.ts b/src/lib/iam/policies/create.ts index 66c5445..0b2be47 100644 --- a/src/lib/iam/policies/create.ts +++ b/src/lib/iam/policies/create.ts @@ -1,17 +1,9 @@ import { existsSync, readFileSync } from 'node:fs'; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig } from '@auth/iam.js'; import { addPolicy, type PolicyDocument } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { parseDocument, readStdin } from './utils.js'; @@ -26,56 +18,19 @@ export default async function create(options: Record) { const description = getOption(options, ['description']) ?? ''; if (!name) { - printFailure(context, 'Policy name is required'); - exitWithError('Policy name is required', context); + failWithError(context, 'Policy name is required'); } // Validate policy name: only alphanumeric and =,.@_- allowed const validNamePattern = /^[a-zA-Z0-9=,.@_-]+$/; if (!validNamePattern.test(name)) { - printFailure( + failWithError( context, 'Invalid policy name. Only alphanumeric characters and =,.@_- are allowed.' ); - exitWithError( - 'Invalid policy name. Only alphanumeric characters and =,.@_- are allowed.', - context - ); - } - - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Policies can only be created when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Policies can only be created when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); } - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); // Get document content let documentJson: string; @@ -92,14 +47,10 @@ export default async function create(options: Record) { // Read from stdin documentJson = await readStdin(); } else { - printFailure( + failWithError( context, 'Policy document is required. Provide via --document or pipe to stdin.' ); - exitWithError( - 'Policy document is required. Provide via --document or pipe to stdin.', - context - ); } // Parse and convert document @@ -107,8 +58,7 @@ export default async function create(options: Record) { try { document = parseDocument(documentJson); } catch { - printFailure(context, 'Invalid JSON in policy document'); - exitWithError('Invalid JSON in policy document', context); + failWithError(context, 'Invalid JSON in policy document'); } const { data, error } = await addPolicy(name, { @@ -118,8 +68,7 @@ export default async function create(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { name: data.name }); diff --git a/src/lib/iam/policies/delete.ts b/src/lib/iam/policies/delete.ts index a4a3bd2..a022c26 100644 --- a/src/lib/iam/policies/delete.ts +++ b/src/lib/iam/policies/delete.ts @@ -1,19 +1,10 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig } from '@auth/iam.js'; import { deletePolicy, listPolicies } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'delete'); @@ -24,39 +15,7 @@ export default async function del(options: Record) { let resource = getOption(options, ['resource']); const force = getOption(options, ['force', 'yes', 'y']); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Policies can only be deleted when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Policies can only be deleted when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); // If no resource provided, list policies and let user select if (!resource) { @@ -65,8 +24,7 @@ export default async function del(options: Record) { }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (!listData.policies || listData.policies.length === 0) { @@ -103,8 +61,7 @@ export default async function del(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { resource }); diff --git a/src/lib/iam/policies/edit.ts b/src/lib/iam/policies/edit.ts index 101598c..14f4e3d 100644 --- a/src/lib/iam/policies/edit.ts +++ b/src/lib/iam/policies/edit.ts @@ -2,25 +2,16 @@ import { existsSync, readFileSync } from 'node:fs'; import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig } from '@auth/iam.js'; import { editPolicy, getPolicy, listPolicies, type PolicyDocument, } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { parseDocument, readStdin } from './utils.js'; @@ -34,52 +25,16 @@ export default async function edit(options: Record) { const documentArg = getOption(options, ['document', 'd']); const description = getOption(options, ['description']); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Policies can only be edited when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Policies can only be edited when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); // If no resource provided, list policies and let user select // But if stdin is piped, we can't use interactive selection if (!resource) { if (!process.stdin.isTTY) { - printFailure( + failWithError( context, 'Policy ARN is required when piping document via stdin.' ); - exitWithError( - 'Policy ARN is required when piping document via stdin.', - context - ); } const { data: listData, error: listError } = await listPolicies({ @@ -87,8 +42,7 @@ export default async function edit(options: Record) { }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (!listData.policies || listData.policies.length === 0) { @@ -126,8 +80,7 @@ export default async function edit(options: Record) { try { newDocument = parseDocument(documentJson); } catch { - printFailure(context, 'Invalid JSON in policy document'); - exitWithError('Invalid JSON in policy document', context); + failWithError(context, 'Invalid JSON in policy document'); } } else if (!process.stdin.isTTY && !description) { // Read from stdin only if no description provided (description-only update doesn't need stdin) @@ -135,14 +88,12 @@ export default async function edit(options: Record) { try { newDocument = parseDocument(documentJson); } catch { - printFailure(context, 'Invalid JSON in policy document'); - exitWithError('Invalid JSON in policy document', context); + failWithError(context, 'Invalid JSON in policy document'); } } if (!newDocument && !description) { - printFailure(context, 'Either --document or --description is required.'); - exitWithError('Either --document or --description is required.', context); + failWithError(context, 'Either --document or --description is required.'); } // Fetch existing policy to fill in missing values @@ -151,8 +102,7 @@ export default async function edit(options: Record) { }); if (getError) { - printFailure(context, getError.message); - exitWithError(getError, context); + failWithError(context, getError); } const { data, error } = await editPolicy(resource, { @@ -162,8 +112,7 @@ export default async function edit(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { resource: data.resource }); diff --git a/src/lib/iam/policies/get.ts b/src/lib/iam/policies/get.ts index fd51cfb..17c44e4 100644 --- a/src/lib/iam/policies/get.ts +++ b/src/lib/iam/policies/get.ts @@ -1,19 +1,10 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig } from '@auth/iam.js'; import { getPolicy, listPolicies } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'get'); @@ -27,39 +18,7 @@ export default async function get(options: Record) { ? 'json' : getOption(options, ['format', 'f', 'F'], 'table'); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Policies can only be retrieved when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Policies can only be retrieved when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); // If no resource provided, list policies and let user select if (!resource) { @@ -68,8 +27,7 @@ export default async function get(options: Record) { }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (!listData.policies || listData.policies.length === 0) { @@ -95,8 +53,7 @@ export default async function get(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/iam/policies/list.ts b/src/lib/iam/policies/list.ts index 5b810d3..dc3992b 100644 --- a/src/lib/iam/policies/list.ts +++ b/src/lib/iam/policies/list.ts @@ -1,17 +1,8 @@ -import { getAuthClient } from '@auth/client.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig } from '@auth/iam.js'; import { listPolicies } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam policies', 'list'); @@ -24,45 +15,18 @@ export default async function list(options: Record) { ? 'json' : getOption(options, ['format', 'f', 'F'], 'table'); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Policies can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Policies can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const selectedOrg = getSelectedOrganization(); - const tigrisConfig = getTigrisConfig(); + const iamConfig = await getOAuthIAMConfig(context); const { data, error } = await listPolicies({ config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, + sessionToken: iamConfig.sessionToken, + organizationId: iamConfig.organizationId, + iamEndpoint: iamConfig.iamEndpoint, }, }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (!data.policies || data.policies.length === 0) { diff --git a/src/lib/iam/users/invite.ts b/src/lib/iam/users/invite.ts index 5025d81..e94b372 100644 --- a/src/lib/iam/users/invite.ts +++ b/src/lib/iam/users/invite.ts @@ -1,16 +1,7 @@ -import { getAuthClient } from '@auth/client.js'; -import { isFlyUser } from '@auth/fly.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { inviteUser } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam users', 'invite'); @@ -18,29 +9,7 @@ const context = msg('iam users', 'invite'); export default async function invite(options: Record) { printStart(context); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Users can only be invited when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Users can only be invited when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const selectedOrg = getSelectedOrganization(); - - if (isFlyUser(selectedOrg ?? undefined)) { - console.log( - 'User management is not available for Fly.io organizations.\n' + - 'Your users are managed through Fly.io.\n\n' + - 'Visit https://fly.io to manage your organization members.' - ); - return; - } + if (isFlyOrganization()) return; const emailOption = getOption(options, ['email']); const roleInput = getOption(options, ['role', 'r']) ?? 'member'; @@ -52,54 +21,31 @@ export default async function invite(options: Record) { : []; if (emails.length === 0) { - printFailure(context, 'Email address is required'); - exitWithError('Email address is required', context); + failWithError(context, 'Email address is required'); } const validRoles = ['admin', 'member'] as const; type Role = (typeof validRoles)[number]; if (!validRoles.includes(roleInput as Role)) { - printFailure( + failWithError( context, `Invalid role "${roleInput}". Must be one of: ${validRoles.join(', ')}` ); - exitWithError( - `Invalid role "${roleInput}". Must be one of: ${validRoles.join(', ')}`, - context - ); } const role: Role = roleInput as Role; - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const tigrisConfig = getTigrisConfig(); + const iamConfig = await getOAuthIAMConfig(context); const invitations = emails.map((email) => ({ email, role })); const { error } = await inviteUser(invitations, { - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - mgmtEndpoint: tigrisConfig.mgmtEndpoint, - }, + config: iamConfig, }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { email: emails.join(', ') }); diff --git a/src/lib/iam/users/list.ts b/src/lib/iam/users/list.ts index 17294af..7ecacad 100644 --- a/src/lib/iam/users/list.ts +++ b/src/lib/iam/users/list.ts @@ -1,23 +1,13 @@ -import { getAuthClient } from '@auth/client.js'; -import { isFlyUser } from '@auth/fly.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { listUsers } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatJson, formatTable, formatXml, type TableColumn, } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam users', 'list'); @@ -30,56 +20,16 @@ export default async function list(options: Record) { ? 'json' : getOption(options, ['format', 'f', 'F'], 'table'); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Users can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Users can only be listed when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const selectedOrg = getSelectedOrganization(); - - if (isFlyUser(selectedOrg ?? undefined)) { - console.log( - 'User management is not available for Fly.io organizations.\n' + - 'Your users are managed through Fly.io.\n\n' + - 'Visit https://fly.io to manage your organization members.' - ); - return; - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } + if (isFlyOrganization()) return; - const accessToken = await authClient.getAccessToken(); - const tigrisConfig = getTigrisConfig(); + const iamConfig = await getOAuthIAMConfig(context); const { data, error } = await listUsers({ - config: { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - mgmtEndpoint: tigrisConfig.mgmtEndpoint, - }, + config: iamConfig, }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const users = data.users.map((user) => ({ diff --git a/src/lib/iam/users/remove.ts b/src/lib/iam/users/remove.ts index c50bb55..eb37f15 100644 --- a/src/lib/iam/users/remove.ts +++ b/src/lib/iam/users/remove.ts @@ -1,20 +1,10 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { isFlyUser } from '@auth/fly.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { listUsers, removeUser as removeUserFromOrg } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam users', 'remove'); @@ -25,50 +15,9 @@ export default async function removeUser(options: Record) { const resourceOption = getOption(options, ['resource']); const force = getOption(options, ['force', 'yes', 'y']); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Users can only be removed when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Users can only be removed when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const selectedOrg = getSelectedOrganization(); - - if (isFlyUser(selectedOrg ?? undefined)) { - console.log( - 'User management is not available for Fly.io organizations.\n' + - 'Your users are managed through Fly.io.\n\n' + - 'Visit https://fly.io to manage your organization members.' - ); - return; - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const tigrisConfig = getTigrisConfig(); + if (isFlyOrganization()) return; - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - mgmtEndpoint: tigrisConfig.mgmtEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); let resources = Array.isArray(resourceOption) ? resourceOption @@ -83,8 +32,7 @@ export default async function removeUser(options: Record) { }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (listData.users.length === 0) { @@ -121,8 +69,7 @@ export default async function removeUser(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context); diff --git a/src/lib/iam/users/revoke-invitation.ts b/src/lib/iam/users/revoke-invitation.ts index 71d5c90..15453f6 100644 --- a/src/lib/iam/users/revoke-invitation.ts +++ b/src/lib/iam/users/revoke-invitation.ts @@ -1,20 +1,10 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { isFlyUser } from '@auth/fly.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { listUsers, revokeInvitation as revokeInv } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam users', 'revoke-invitation'); @@ -27,50 +17,9 @@ export default async function revokeInvitation( const resourceOption = getOption(options, ['resource']); const force = getOption(options, ['force', 'yes', 'y']); - const loginMethod = await getLoginMethod(); + if (isFlyOrganization()) return; - if (loginMethod !== 'oauth') { - printFailure( - context, - 'Invitations can only be revoked when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'Invitations can only be revoked when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const selectedOrg = getSelectedOrganization(); - - if (isFlyUser(selectedOrg ?? undefined)) { - console.log( - 'User management is not available for Fly.io organizations.\n' + - 'Your users are managed through Fly.io.\n\n' + - 'Visit https://fly.io to manage your organization members.' - ); - return; - } - - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - mgmtEndpoint: tigrisConfig.mgmtEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); let resources = Array.isArray(resourceOption) ? resourceOption @@ -85,8 +34,7 @@ export default async function revokeInvitation( }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (listData.invitations.length === 0) { @@ -126,8 +74,7 @@ export default async function revokeInvitation( }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context); diff --git a/src/lib/iam/users/update-role.ts b/src/lib/iam/users/update-role.ts index 56bc63e..6def3b9 100644 --- a/src/lib/iam/users/update-role.ts +++ b/src/lib/iam/users/update-role.ts @@ -1,20 +1,10 @@ import enquirer from 'enquirer'; const { prompt } = enquirer; -import { getAuthClient } from '@auth/client.js'; -import { isFlyUser } from '@auth/fly.js'; -import { getLoginMethod } from '@auth/provider.js'; -import { getTigrisConfig } from '@auth/provider.js'; -import { getSelectedOrganization } from '@auth/storage.js'; +import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { listUsers, updateUserRole } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('iam users', 'update-role'); @@ -22,29 +12,7 @@ const context = msg('iam users', 'update-role'); export default async function updateRole(options: Record) { printStart(context); - const loginMethod = await getLoginMethod(); - - if (loginMethod !== 'oauth') { - printFailure( - context, - 'User roles can only be updated when logged in via OAuth.\nRun "tigris login oauth" first.' - ); - exitWithError( - 'User roles can only be updated when logged in via OAuth.\nRun "tigris login oauth" first.', - context - ); - } - - const selectedOrg = getSelectedOrganization(); - - if (isFlyUser(selectedOrg ?? undefined)) { - console.log( - 'User management is not available for Fly.io organizations.\n' + - 'Your users are managed through Fly.io.\n\n' + - 'Visit https://fly.io to manage your organization members.' - ); - return; - } + if (isFlyOrganization()) return; const validRoles = ['admin', 'member'] as const; type Role = (typeof validRoles)[number]; @@ -52,28 +20,20 @@ export default async function updateRole(options: Record) { const roleOption = getOption(options, ['role', 'r']); if (!roleOption) { - printFailure( + failWithError( context, 'Role is required. Use --role admin or --role member' ); - exitWithError( - 'Role is required. Use --role admin or --role member', - context - ); } const roles = Array.isArray(roleOption) ? roleOption : [roleOption]; for (const r of roles) { if (!validRoles.includes(r as Role)) { - printFailure( + failWithError( context, `Invalid role "${r}". Must be one of: ${validRoles.join(', ')}` ); - exitWithError( - `Invalid role "${r}". Must be one of: ${validRoles.join(', ')}`, - context - ); } } @@ -85,26 +45,7 @@ export default async function updateRole(options: Record) { ? [resourceOption] : []; - const authClient = getAuthClient(); - const isAuthenticated = await authClient.isAuthenticated(); - - if (!isAuthenticated) { - printFailure(context, 'Not authenticated. Run "tigris login oauth" first.'); - exitWithError( - 'Not authenticated. Run "tigris login oauth" first.', - context - ); - } - - const accessToken = await authClient.getAccessToken(); - const tigrisConfig = getTigrisConfig(); - - const iamConfig = { - sessionToken: accessToken, - organizationId: selectedOrg ?? undefined, - iamEndpoint: tigrisConfig.iamEndpoint, - mgmtEndpoint: tigrisConfig.mgmtEndpoint, - }; + const iamConfig = await getOAuthIAMConfig(context); // If no resource provided, list users and let user select if (resources.length === 0) { @@ -113,8 +54,7 @@ export default async function updateRole(options: Record) { }); if (listError) { - printFailure(context, listError.message); - exitWithError(listError, context); + failWithError(context, listError); } if (listData.users.length === 0) { @@ -139,14 +79,10 @@ export default async function updateRole(options: Record) { // Pair roles with users: if one role given, apply to all; otherwise pair 1:1 if (roles.length > 1 && roles.length !== resources.length) { - printFailure( + failWithError( context, `Number of roles (${roles.length}) must match number of users (${resources.length}), or provide a single role for all users` ); - exitWithError( - `Number of roles (${roles.length}) must match number of users (${resources.length}), or provide a single role for all users`, - context - ); } const roleUpdates = resources.map((userId, i) => ({ @@ -159,8 +95,7 @@ export default async function updateRole(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context); diff --git a/src/lib/login/credentials.ts b/src/lib/login/credentials.ts index 8ab7ad7..3ab11c1 100644 --- a/src/lib/login/credentials.ts +++ b/src/lib/login/credentials.ts @@ -8,14 +8,9 @@ import { storeTemporaryCredentials, } from '@auth/storage.js'; import { whoami } from '@tigrisdata/iam'; -import { exitWithError, printNextActions } from '@utils/exit.js'; +import { failWithError, printNextActions } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; @@ -77,8 +72,7 @@ export default async function credentials(options: Record) { // Validate if (!accessKey || !accessSecret) { - printFailure(context, 'Access key and secret are required'); - exitWithError('Access key and secret are required', context); + failWithError(context, 'Access key and secret are required'); } // Get endpoint: configured → default diff --git a/src/lib/logout.ts b/src/lib/logout.ts index cb43b22..14e8738 100644 --- a/src/lib/logout.ts +++ b/src/lib/logout.ts @@ -1,11 +1,6 @@ import { clearAllData } from '@auth/storage.js'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; const context = msg('logout'); @@ -17,11 +12,6 @@ export default async function logout(): Promise { printSuccess(context); } catch (error) { - if (error instanceof Error) { - printFailure(context, error.message); - } else { - printFailure(context); - } - exitWithError(error, context); + failWithError(context, error); } } diff --git a/src/lib/objects/delete.ts b/src/lib/objects/delete.ts index 1a412f3..7a3c431 100644 --- a/src/lib/objects/delete.ts +++ b/src/lib/objects/delete.ts @@ -2,6 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { remove } from '@tigrisdata/storage'; import { exitWithError, + failWithError, getSuccessNextActions, printNextActions, } from '@utils/exit.js'; @@ -29,13 +30,11 @@ export default async function deleteObject(options: Record) { const force = getOption(options, ['force', 'yes', 'y']); if (!bucket) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (!keys) { - printFailure(context, 'Object key is required'); - exitWithError('Object key is required', context); + failWithError(context, 'Object key is required'); } const config = await getStorageConfig(); diff --git a/src/lib/objects/get.ts b/src/lib/objects/get.ts index dc855b4..55086e0 100644 --- a/src/lib/objects/get.ts +++ b/src/lib/objects/get.ts @@ -1,12 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { get } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { createWriteStream, writeFileSync } from 'fs'; import { extname } from 'path'; @@ -125,13 +120,11 @@ export default async function getObject(options: Record) { ]); if (!bucket) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (!key) { - printFailure(context, 'Object key is required'); - exitWithError('Object key is required', context); + failWithError(context, 'Object key is required'); } const config = await getStorageConfig(); @@ -149,8 +142,7 @@ export default async function getObject(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (output) { @@ -177,8 +169,7 @@ export default async function getObject(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (output) { diff --git a/src/lib/objects/list.ts b/src/lib/objects/list.ts index c3e866d..3502995 100644 --- a/src/lib/objects/list.ts +++ b/src/lib/objects/list.ts @@ -1,14 +1,8 @@ import { getStorageConfig } from '@auth/provider.js'; import { list } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('objects', 'list'); @@ -29,8 +23,7 @@ export default async function listObjects(options: Record) { ]); if (!bucket) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const config = await getStorageConfig(); @@ -45,8 +38,7 @@ export default async function listObjects(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (!data.items || data.items.length === 0) { diff --git a/src/lib/objects/put.ts b/src/lib/objects/put.ts index 4bbe5b2..a30f265 100644 --- a/src/lib/objects/put.ts +++ b/src/lib/objects/put.ts @@ -1,13 +1,8 @@ import { getStorageConfig } from '@auth/provider.js'; import { put } from '@tigrisdata/storage'; -import { exitWithError, printNextActions } from '@utils/exit.js'; +import { failWithError, printNextActions } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { calculateUploadParams } from '@utils/upload.js'; import { createReadStream, statSync } from 'fs'; @@ -34,21 +29,18 @@ export default async function putObject(options: Record) { : getOption(options, ['format', 'f', 'F'], 'table'); if (!bucket) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (!key) { - printFailure(context, 'Object key is required'); - exitWithError('Object key is required', context); + failWithError(context, 'Object key is required'); } // Check for stdin or file input const hasStdin = !process.stdin.isTTY; if (!file && !hasStdin) { - printFailure(context, 'File path is required (or pipe data via stdin)'); - exitWithError('File path is required (or pipe data via stdin)', context); + failWithError(context, 'File path is required (or pipe data via stdin)'); } let body: ReadableStream; @@ -60,8 +52,7 @@ export default async function putObject(options: Record) { const stats = statSync(file); fileSize = stats.size; } catch { - printFailure(context, `File not found: ${file}`); - exitWithError(`File not found: ${file}`, context); + failWithError(context, `File not found: ${file}`); } const fileStream = createReadStream(file); body = Readable.toWeb(fileStream) as ReadableStream; @@ -100,8 +91,7 @@ export default async function putObject(options: Record) { process.stdout.write('\r' + ' '.repeat(60) + '\r'); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const result = [ diff --git a/src/lib/objects/set.ts b/src/lib/objects/set.ts index 293563a..86d6c31 100644 --- a/src/lib/objects/set.ts +++ b/src/lib/objects/set.ts @@ -1,12 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { updateObject } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('objects', 'set'); @@ -25,20 +20,17 @@ export default async function setObject(options: Record) { const newKey = getOption(options, ['new-key', 'n', 'newKey']); if (!bucket) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } if (!key) { - printFailure(context, 'Object key is required'); - exitWithError('Object key is required', context); + failWithError(context, 'Object key is required'); } if (!access) { - printFailure(context, 'Access level is required (--access public|private)'); - exitWithError( - 'Access level is required (--access public|private)', - context + failWithError( + context, + 'Access level is required (--access public|private)' ); } @@ -54,8 +46,7 @@ export default async function setObject(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (format === 'json') { diff --git a/src/lib/organizations/create.ts b/src/lib/organizations/create.ts index 25ac84e..ce24cf7 100644 --- a/src/lib/organizations/create.ts +++ b/src/lib/organizations/create.ts @@ -1,19 +1,9 @@ import { isFlyUser } from '@auth/fly.js'; -import { getStorageConfig } from '@auth/provider.js'; -import { - getCredentials, - getLoginMethod, - getSelectedOrganization, -} from '@auth/storage.js'; +import { getStorageConfig, requireOAuthLogin } from '@auth/provider.js'; +import { getSelectedOrganization } from '@auth/storage.js'; import { createOrganization } from '@tigrisdata/iam'; -import { exitWithError, printNextActions } from '@utils/exit.js'; -import { - msg, - printFailure, - printHint, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError, printNextActions } from '@utils/exit.js'; +import { msg, printHint, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('organizations', 'create'); @@ -21,23 +11,7 @@ const context = msg('organizations', 'create'); export default async function create(options: Record) { printStart(context); - // Check if logged in with OAuth (required for org creation) - const loginMethod = getLoginMethod(); - if (loginMethod !== 'oauth') { - // Not logged in via OAuth - check if using credentials - if (getCredentials()) { - console.log( - 'You are using access key credentials, which belong to a single organization.\n' + - 'Organization creation is only available with OAuth login.\n\n' + - 'Run "tigris login" to login with your Tigris account.' - ); - } else { - console.log( - 'Not authenticated. Please run "tigris login" to login with your Tigris account.' - ); - } - return; - } + if (requireOAuthLogin('Organization creation')) return; // Fly users cannot create organizations const selectedOrg = getSelectedOrganization(); @@ -53,8 +27,7 @@ export default async function create(options: Record) { const name = getOption(options, ['name', 'N']); if (!name) { - printFailure(context, 'Organization name is required'); - exitWithError('Organization name is required', context); + failWithError(context, 'Organization name is required'); } const config = await getStorageConfig(); @@ -62,8 +35,7 @@ export default async function create(options: Record) { const { data, error } = await createOrganization(name, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const id = data.id; diff --git a/src/lib/organizations/list.ts b/src/lib/organizations/list.ts index e3ea7a6..a9e3f78 100644 --- a/src/lib/organizations/list.ts +++ b/src/lib/organizations/list.ts @@ -1,23 +1,15 @@ import { getAuthClient } from '@auth/client.js'; import { fetchOrganizationsFromUserInfo, isFlyUser } from '@auth/fly.js'; -import { getStorageConfig } from '@auth/provider.js'; +import { getStorageConfig, requireOAuthLogin } from '@auth/provider.js'; import { - getCredentials, - getLoginMethod, getSelectedOrganization, storeSelectedOrganization, } from '@auth/storage.js'; import { listOrganizations } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { requireInteractive } from '@utils/interactive.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import Enquirer from 'enquirer'; @@ -26,23 +18,7 @@ const context = msg('organizations', 'list'); export default async function list(options: Record) { printStart(context); - // Check if logged in with OAuth (required for org listing) - const loginMethod = getLoginMethod(); - if (loginMethod !== 'oauth') { - // Not logged in via OAuth - check if using credentials - if (getCredentials()) { - console.log( - 'You are using access key credentials, which belong to a single organization.\n' + - 'Organization listing and selection is only available with OAuth login.\n\n' + - 'Run "tigris login" to login with your Tigris account.' - ); - } else { - console.log( - 'Not authenticated. Please run "tigris login" to login with your Tigris account.' - ); - } - return; - } + if (requireOAuthLogin('Organization listing and selection')) return; const json = getOption(options, ['json']); const format = json @@ -68,8 +44,7 @@ export default async function list(options: Record) { const { data, error } = await listOrganizations({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } orgs = data?.organizations ?? []; diff --git a/src/lib/organizations/select.ts b/src/lib/organizations/select.ts index 98c3c73..4632a05 100644 --- a/src/lib/organizations/select.ts +++ b/src/lib/organizations/select.ts @@ -1,11 +1,7 @@ -import { getStorageConfig } from '@auth/provider.js'; -import { - getCredentials, - getLoginMethod, - storeSelectedOrganization, -} from '@auth/storage.js'; +import { getStorageConfig, requireOAuthLogin } from '@auth/provider.js'; +import { storeSelectedOrganization } from '@auth/storage.js'; import { listOrganizations } from '@tigrisdata/iam'; -import { exitWithError, printNextActions } from '@utils/exit.js'; +import { exitWithError, failWithError, printNextActions } from '@utils/exit.js'; import { msg, printFailure, @@ -19,29 +15,12 @@ const context = msg('organizations', 'select'); export default async function select(options: Record) { printStart(context); - // Check if logged in with OAuth (required for org selection) - const loginMethod = getLoginMethod(); - if (loginMethod !== 'oauth') { - // Not logged in via OAuth - check if using credentials - if (getCredentials()) { - console.log( - 'You are using access key credentials, which belong to a single organization.\n' + - 'Organization selection is only available with OAuth login.\n\n' + - 'Run "tigris login" to login with your Tigris account.' - ); - } else { - console.log( - 'Not authenticated. Please run "tigris login" to login with your Tigris account.' - ); - } - return; - } + if (requireOAuthLogin('Organization selection')) return; const name = getOption(options, ['name', 'N']); if (!name) { - printFailure(context, 'Organization name or ID is required'); - exitWithError('Organization name or ID is required', context); + failWithError(context, 'Organization name or ID is required'); } const config = await getStorageConfig(); @@ -49,8 +28,7 @@ export default async function select(options: Record) { const { data, error } = await listOrganizations({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const orgs = data?.organizations ?? []; diff --git a/src/lib/snapshots/list.ts b/src/lib/snapshots/list.ts index d814f98..289d22b 100644 --- a/src/lib/snapshots/list.ts +++ b/src/lib/snapshots/list.ts @@ -1,14 +1,8 @@ import { getStorageConfig } from '@auth/provider.js'; import { listBucketSnapshots } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; -import { - msg, - printEmpty, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('snapshots', 'list'); @@ -23,8 +17,7 @@ export default async function list(options: Record) { : getOption(options, ['format', 'f', 'F'], 'table'); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const config = await getStorageConfig(); @@ -32,8 +25,7 @@ export default async function list(options: Record) { const { data, error } = await listBucketSnapshots(name, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (!data || data.length === 0) { diff --git a/src/lib/snapshots/take.ts b/src/lib/snapshots/take.ts index 6cc2648..6ad7af3 100644 --- a/src/lib/snapshots/take.ts +++ b/src/lib/snapshots/take.ts @@ -1,12 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { createBucketSnapshot } from '@tigrisdata/storage'; -import { exitWithError } from '@utils/exit.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('snapshots', 'take'); @@ -21,8 +16,7 @@ export default async function take(options: Record) { ]); if (!name) { - printFailure(context, 'Bucket name is required'); - exitWithError('Bucket name is required', context); + failWithError(context, 'Bucket name is required'); } const config = await getStorageConfig(); @@ -33,8 +27,7 @@ export default async function take(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } printSuccess(context, { diff --git a/src/lib/stat.ts b/src/lib/stat.ts index dfab326..51951ee 100644 --- a/src/lib/stat.ts +++ b/src/lib/stat.ts @@ -1,14 +1,9 @@ import { getStorageConfig } from '@auth/provider.js'; import { getBucketInfo, getStats, head } from '@tigrisdata/storage'; import { buildBucketInfo } from '@utils/bucket-info.js'; -import { exitWithError } from '@utils/exit.js'; +import { failWithError } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; -import { - msg, - printFailure, - printStart, - printSuccess, -} from '@utils/messages.js'; +import { msg, printStart, printSuccess } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; @@ -34,8 +29,7 @@ export default async function stat(options: Record) { const { data, error } = await getStats({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const stats = [ @@ -64,8 +58,7 @@ export default async function stat(options: Record) { const { bucket, path } = parseAnyPath(pathString); if (!bucket) { - printFailure(context, 'Invalid path'); - exitWithError('Invalid path', context); + failWithError(context, 'Invalid path'); } // Bucket only (no path or just trailing slash): show bucket info @@ -73,8 +66,7 @@ export default async function stat(options: Record) { const { data, error } = await getBucketInfo(bucket, { config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } const info = buildBucketInfo(data).map(({ label, value }) => ({ @@ -102,13 +94,11 @@ export default async function stat(options: Record) { }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } if (!data) { - printFailure(context, 'Object not found'); - exitWithError('Object not found', context); + failWithError(context, 'Object not found'); } const info = [ diff --git a/src/lib/whoami.ts b/src/lib/whoami.ts index 4592672..01f234f 100644 --- a/src/lib/whoami.ts +++ b/src/lib/whoami.ts @@ -6,8 +6,8 @@ import { getSelectedOrganization, } from '@auth/storage.js'; import { listOrganizations } from '@tigrisdata/iam'; -import { exitWithError } from '@utils/exit.js'; -import { msg, printAlreadyDone, printFailure } from '@utils/messages.js'; +import { failWithError } from '@utils/exit.js'; +import { msg, printAlreadyDone } from '@utils/messages.js'; import { getOption } from '@utils/options.js'; const context = msg('whoami'); @@ -64,8 +64,7 @@ export default async function whoami( const { data, error } = await listOrganizations({ config }); if (error) { - printFailure(context, error.message); - exitWithError(error, context); + failWithError(context, error); } organizations = data?.organizations ?? []; @@ -117,11 +116,6 @@ export default async function whoami( lines.push(''); console.log(lines.join('\n')); } catch (error) { - if (error instanceof Error) { - printFailure(context, error.message); - } else { - printFailure(context); - } - exitWithError(error, context); + failWithError(context, error); } } diff --git a/src/utils/exit.ts b/src/utils/exit.ts index 9cae058..24d6409 100644 --- a/src/utils/exit.ts +++ b/src/utils/exit.ts @@ -1,7 +1,7 @@ import type { NextAction } from '../types.js'; import { classifyError } from './errors.js'; import type { MessageContext, MessageVariables } from './messages.js'; -import { interpolate } from './messages.js'; +import { interpolate, printFailure } from './messages.js'; import { getCommandSpec } from './specs.js'; function isJsonMode(): boolean { @@ -54,6 +54,15 @@ export function exitWithError(error: unknown, context?: MessageContext): never { process.exit(classified.exitCode); } +/** + * Print failure message and exit. Combines printFailure + exitWithError. + */ +export function failWithError(context: MessageContext, error: unknown): never { + const message = error instanceof Error ? error.message : String(error); + printFailure(context, message); + exitWithError(error, context); +} + /** * Read nextActions from specs.yaml for a command and interpolate variables. * Returns empty array if no nextActions defined. From 8200b829ced4757dac37d8e123a75efafa04ce03 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Sun, 29 Mar 2026 19:45:43 +0200 Subject: [PATCH 4/5] build: trigger patch release on refactor commits Co-Authored-By: Claude Opus 4.6 --- package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b47a3e9..7cdc2d7 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,11 @@ } ], "plugins": [ - "@semantic-release/commit-analyzer", + ["@semantic-release/commit-analyzer", { + "releaseRules": [ + { "type": "refactor", "release": "patch" } + ] + }], "@semantic-release/release-notes-generator", "@semantic-release/github", "@semantic-release/npm" From c6cc37151d8af3c6653da2c6b3e7bb73fd66c5dd Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Mon, 30 Mar 2026 10:49:14 +0200 Subject: [PATCH 5/5] refactor: auto-inject global arguments and add getFormat helper Define --json, --format, and --yes once in specs.yaml and auto-inject them into every leaf command via getEffectiveArguments(). Replace the repeated 3-line json/format boilerplate across 52 files with a single getFormat() call. Unify --force flags to --yes globally. Co-Authored-By: Claude Opus 4.6 --- src/cli-core.ts | 42 ++++- src/lib/access-keys/assign.ts | 7 +- src/lib/access-keys/create.ts | 7 +- src/lib/access-keys/delete.ts | 9 +- src/lib/access-keys/get.ts | 7 +- src/lib/access-keys/list.ts | 7 +- src/lib/buckets/create.ts | 7 +- src/lib/buckets/delete.ts | 9 +- src/lib/buckets/get.ts | 7 +- src/lib/buckets/list.ts | 7 +- src/lib/buckets/set-cors.ts | 8 +- src/lib/buckets/set-locations.ts | 8 +- src/lib/buckets/set-migration.ts | 12 +- src/lib/buckets/set-notifications.ts | 8 +- src/lib/buckets/set-transition.ts | 8 +- src/lib/buckets/set-ttl.ts | 8 +- src/lib/buckets/set.ts | 7 +- src/lib/configure/index.ts | 7 + src/lib/cp.ts | 7 +- src/lib/credentials/test.ts | 7 +- src/lib/forks/create.ts | 10 +- src/lib/forks/list.ts | 7 +- src/lib/iam/policies/create.ts | 10 +- src/lib/iam/policies/delete.ts | 10 +- src/lib/iam/policies/edit.ts | 8 +- src/lib/iam/policies/get.ts | 7 +- src/lib/iam/policies/list.ts | 7 +- src/lib/iam/users/invite.ts | 10 +- src/lib/iam/users/list.ts | 7 +- src/lib/iam/users/remove.ts | 10 +- src/lib/iam/users/revoke-invitation.ts | 10 +- src/lib/iam/users/update-role.ts | 8 +- src/lib/login/credentials.ts | 7 + src/lib/login/oauth.ts | 16 +- src/lib/logout.ts | 12 +- src/lib/ls.ts | 7 +- src/lib/mk.ts | 7 +- src/lib/mv.ts | 9 +- src/lib/objects/delete.ts | 9 +- src/lib/objects/get.ts | 7 +- src/lib/objects/list.ts | 7 +- src/lib/objects/put.ts | 7 +- src/lib/objects/set.ts | 7 +- src/lib/organizations/create.ts | 8 +- src/lib/organizations/list.ts | 7 +- src/lib/organizations/select.ts | 8 +- src/lib/presign.ts | 7 +- src/lib/rm.ts | 9 +- src/lib/snapshots/list.ts | 7 +- src/lib/snapshots/take.ts | 14 +- src/lib/stat.ts | 7 +- src/lib/touch.ts | 7 +- src/lib/whoami.ts | 7 +- src/specs.yaml | 217 ++----------------------- src/types.ts | 4 + src/utils/options.ts | 16 ++ 56 files changed, 312 insertions(+), 394 deletions(-) diff --git a/src/cli-core.ts b/src/cli-core.ts index 2edf6fc..2a9cea7 100644 --- a/src/cli-core.ts +++ b/src/cli-core.ts @@ -160,9 +160,11 @@ export function showCommandHelp( } } - if (command.arguments && command.arguments.length > 0) { + const globalArgs = specs.definitions?.global_arguments ?? []; + const effectiveArgs = getEffectiveArguments(globalArgs, command.arguments); + if (effectiveArgs.length > 0) { console.log('Arguments:'); - command.arguments.forEach((arg) => { + effectiveArgs.forEach((arg) => { console.log(formatArgumentHelp(arg)); }); console.log(); @@ -212,6 +214,27 @@ export function showMainHelp( ); } +/** + * Merge global arguments (from specs.yaml definitions.global_arguments) + * into a command's argument list, skipping any that the command already + * defines by name or whose alias collides with an existing argument's alias. + */ +function getEffectiveArguments( + globalArgs: Argument[], + specArgs?: Argument[] +): Argument[] { + const args = specArgs ?? []; + const definedNames = new Set(args.map((a) => a.name)); + const definedAliases = new Set( + args.filter((a) => a.alias).map((a) => a.alias) + ); + const injected = globalArgs.filter( + (g) => + !definedNames.has(g.name) && !(g.alias && definedAliases.has(g.alias)) + ); + return [...args, ...injected]; +} + export function addArgumentsToCommand( cmd: CommanderCommand, args: Argument[] = [] @@ -399,6 +422,7 @@ export function registerCommands( pathParts: string[] = [] ) { const { specs, loadModule, hasImplementation } = config; + const globalArgs = specs.definitions?.global_arguments ?? []; for (const spec of commandSpecs) { if (!isValidCommandName(spec.name)) { @@ -429,13 +453,11 @@ export function registerCommands( if (spec.default) { const defaultCmd = spec.commands.find((c) => c.name === spec.default); if (defaultCmd) { - addArgumentsToCommand(cmd, spec.arguments); - addArgumentsToCommand(cmd, defaultCmd.arguments); - - const allArguments = [ + const allArguments = getEffectiveArguments(globalArgs, [ ...(spec.arguments || []), ...(defaultCmd.arguments || []), - ]; + ]); + addArgumentsToCommand(cmd, allArguments); cmd.action(async (...args) => { const options = args.pop(); @@ -470,7 +492,10 @@ export function registerCommands( } } else { // Leaf command - addArgumentsToCommand(cmd, spec.arguments); + addArgumentsToCommand( + cmd, + getEffectiveArguments(globalArgs, spec.arguments) + ); cmd.action(async (...args) => { const options = args.pop(); @@ -517,7 +542,6 @@ export function createProgram(config: CLIConfig): CommanderCommand { const program = new CommanderCommand(); program.name(specs.name).description(specs.description).version(version); - program.option('-y, --yes', 'Skip all confirmation prompts'); registerCommands(config, program, specs.commands); diff --git a/src/lib/access-keys/assign.ts b/src/lib/access-keys/assign.ts index da9f671..a82b8db 100644 --- a/src/lib/access-keys/assign.ts +++ b/src/lib/access-keys/assign.ts @@ -6,7 +6,7 @@ import { printNextActions, } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('access-keys', 'assign'); @@ -21,10 +21,7 @@ function normalizeToArray(value: T | T[] | undefined): T[] { export default async function assign(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const id = getOption(options, ['id']); const admin = getOption(options, ['admin']); diff --git a/src/lib/access-keys/create.ts b/src/lib/access-keys/create.ts index fb9e87e..dae4d22 100644 --- a/src/lib/access-keys/create.ts +++ b/src/lib/access-keys/create.ts @@ -6,17 +6,14 @@ import { printNextActions, } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('access-keys', 'create'); export default async function create(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const name = getOption(options, ['name']); diff --git a/src/lib/access-keys/delete.ts b/src/lib/access-keys/delete.ts index 8b35fa7..adef649 100644 --- a/src/lib/access-keys/delete.ts +++ b/src/lib/access-keys/delete.ts @@ -3,20 +3,17 @@ import { removeAccessKey } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('access-keys', 'delete'); export default async function remove(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const id = getOption(options, ['id']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); if (!id) { failWithError(context, 'Access key ID is required'); diff --git a/src/lib/access-keys/get.ts b/src/lib/access-keys/get.ts index 8fc9b45..3ecf97f 100644 --- a/src/lib/access-keys/get.ts +++ b/src/lib/access-keys/get.ts @@ -2,17 +2,14 @@ import { getIAMConfig } from '@auth/iam.js'; import { getAccessKey } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('access-keys', 'get'); export default async function get(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const id = getOption(options, ['id']); diff --git a/src/lib/access-keys/list.ts b/src/lib/access-keys/list.ts index 74093ee..d113f3b 100644 --- a/src/lib/access-keys/list.ts +++ b/src/lib/access-keys/list.ts @@ -3,17 +3,14 @@ import { listAccessKeys } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat } from '@utils/options.js'; const context = msg('access-keys', 'list'); export default async function list(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const config = await getIAMConfig(context); diff --git a/src/lib/buckets/create.ts b/src/lib/buckets/create.ts index 7b41ec2..5d15266 100644 --- a/src/lib/buckets/create.ts +++ b/src/lib/buckets/create.ts @@ -9,7 +9,7 @@ import { import { requireInteractive } from '@utils/interactive.js'; import { parseLocations, promptLocations } from '@utils/locations.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { buildPromptChoices, getArgumentSpec } from '@utils/specs.js'; import enquirer from 'enquirer'; @@ -20,10 +20,7 @@ const context = msg('buckets', 'create'); export default async function create(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); let name = getOption(options, ['name']); const isPublic = getOption(options, ['public']); diff --git a/src/lib/buckets/delete.ts b/src/lib/buckets/delete.ts index b26d08a..9a3a1b8 100644 --- a/src/lib/buckets/delete.ts +++ b/src/lib/buckets/delete.ts @@ -13,20 +13,17 @@ import { printStart, printSuccess, } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'delete'); export default async function deleteBucket(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const names = getOption(options, ['name']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); if (!names) { failWithError(context, 'Bucket name is required'); diff --git a/src/lib/buckets/get.ts b/src/lib/buckets/get.ts index 7292768..f03d533 100644 --- a/src/lib/buckets/get.ts +++ b/src/lib/buckets/get.ts @@ -4,7 +4,7 @@ import { buildBucketInfo } from '@utils/bucket-info.js'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'get'); @@ -12,10 +12,7 @@ export default async function get(options: Record) { printStart(context); const name = getOption(options, ['name']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F']) || 'table'; + const format = getFormat(options); if (!name) { failWithError(context, 'Bucket name is required'); diff --git a/src/lib/buckets/list.ts b/src/lib/buckets/list.ts index 9ab67c1..83a3592 100644 --- a/src/lib/buckets/list.ts +++ b/src/lib/buckets/list.ts @@ -3,17 +3,14 @@ import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'list'); export default async function list(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const forksOf = getOption(options, ['forks-of', 'forksOf']); const config = await getStorageConfig(); diff --git a/src/lib/buckets/set-cors.ts b/src/lib/buckets/set-cors.ts index 0432032..02e2f41 100644 --- a/src/lib/buckets/set-cors.ts +++ b/src/lib/buckets/set-cors.ts @@ -2,13 +2,15 @@ import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketCors } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-cors'); export default async function setCors(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const origins = getOption(options, ['origins']); const methods = getOption(options, ['methods']); @@ -67,5 +69,9 @@ export default async function setCors(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set-locations.ts b/src/lib/buckets/set-locations.ts index c1998db..e94ac99 100644 --- a/src/lib/buckets/set-locations.ts +++ b/src/lib/buckets/set-locations.ts @@ -5,13 +5,15 @@ import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { parseLocations, promptLocations } from '@utils/locations.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-locations'); export default async function setLocations(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const locations = getOption(options, ['locations']); @@ -42,5 +44,9 @@ export default async function setLocations(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set-migration.ts b/src/lib/buckets/set-migration.ts index d3aa6f3..d7d6cb4 100644 --- a/src/lib/buckets/set-migration.ts +++ b/src/lib/buckets/set-migration.ts @@ -2,13 +2,15 @@ import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketMigration } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-migration'); export default async function setMigration(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const bucket = getOption(options, ['bucket']); const endpoint = getOption(options, ['endpoint']); @@ -49,6 +51,10 @@ export default async function setMigration(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); return; } @@ -77,5 +83,9 @@ export default async function setMigration(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set-notifications.ts b/src/lib/buckets/set-notifications.ts index ec54e90..509d0b1 100644 --- a/src/lib/buckets/set-notifications.ts +++ b/src/lib/buckets/set-notifications.ts @@ -5,7 +5,7 @@ import { } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-notifications'); @@ -14,6 +14,8 @@ export default async function setNotifications( ) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const url = getOption(options, ['url']); const filter = getOption(options, ['filter']); @@ -107,5 +109,9 @@ export default async function setNotifications( failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set-transition.ts b/src/lib/buckets/set-transition.ts index 581677d..ff9a3b0 100644 --- a/src/lib/buckets/set-transition.ts +++ b/src/lib/buckets/set-transition.ts @@ -5,7 +5,7 @@ import { } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-transition'); @@ -14,6 +14,8 @@ const VALID_TRANSITION_CLASSES = ['STANDARD_IA', 'GLACIER', 'GLACIER_IR']; export default async function setTransitions(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const storageClass = getOption(options, [ 'storage-class', @@ -98,5 +100,9 @@ export default async function setTransitions(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set-ttl.ts b/src/lib/buckets/set-ttl.ts index 56421e3..941bda9 100644 --- a/src/lib/buckets/set-ttl.ts +++ b/src/lib/buckets/set-ttl.ts @@ -2,13 +2,15 @@ import { getStorageConfigWithOrg } from '@auth/provider.js'; import { setBucketTtl } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('buckets', 'set-ttl'); export default async function setTtl(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const days = getOption(options, ['days']); const date = getOption(options, ['date']); @@ -66,5 +68,9 @@ export default async function setTtl(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', bucket: name })); + } + printSuccess(context, { name }); } diff --git a/src/lib/buckets/set.ts b/src/lib/buckets/set.ts index 4871184..1dcc632 100644 --- a/src/lib/buckets/set.ts +++ b/src/lib/buckets/set.ts @@ -3,17 +3,14 @@ import { updateBucket, type UpdateBucketOptions } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { parseLocations } from '@utils/locations.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption, parseBoolean } from '@utils/options.js'; +import { getFormat, getOption, parseBoolean } from '@utils/options.js'; const context = msg('buckets', 'set'); export default async function set(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format'], 'table'); + const format = getFormat(options); const name = getOption(options, ['name']); const access = getOption(options, ['access']); diff --git a/src/lib/configure/index.ts b/src/lib/configure/index.ts index 530db73..a0dd136 100644 --- a/src/lib/configure/index.ts +++ b/src/lib/configure/index.ts @@ -15,6 +15,7 @@ import { printStart, printSuccess, } from '@utils/messages.js'; +import { getFormat } from '@utils/options.js'; import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; @@ -23,6 +24,8 @@ const context = msg('configure'); export default async function configure(options: Record) { printStart(context); + const format = getFormat(options); + let accessKey = options['access-key'] || options['accessKey'] || @@ -116,6 +119,10 @@ export default async function configure(options: Record) { // Non-fatal — org will just be missing } + if (format === 'json') { + console.log(JSON.stringify({ action: 'configured' })); + } + printSuccess(context); printNextActions(context); } catch (error) { diff --git a/src/lib/cp.ts b/src/lib/cp.ts index 64692e7..879bc80 100644 --- a/src/lib/cp.ts +++ b/src/lib/cp.ts @@ -3,7 +3,7 @@ import { get, head, list, put } from '@tigrisdata/storage'; import { executeWithConcurrency } from '@utils/concurrency.js'; import { exitWithError } from '@utils/exit.js'; import { formatSize } from '@utils/format.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { globToRegex, isPathFolder, @@ -854,10 +854,7 @@ export default async function cp(options: Record) { } const recursive = !!getOption(options, ['recursive', 'r']); - const jsonFlag = getOption(options, ['json']); - const format = jsonFlag - ? 'json' - : getOption(options, ['format'], 'table'); + const format = getFormat(options); _jsonMode = format === 'json'; const direction = detectDirection(src, dest); diff --git a/src/lib/credentials/test.ts b/src/lib/credentials/test.ts index 57c719a..1651cdd 100644 --- a/src/lib/credentials/test.ts +++ b/src/lib/credentials/test.ts @@ -8,17 +8,14 @@ import { printStart, printSuccess, } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('credentials', 'test'); export default async function test(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const bucket = getOption(options, ['bucket', 'b']); diff --git a/src/lib/forks/create.ts b/src/lib/forks/create.ts index ef252d6..eea4ef6 100644 --- a/src/lib/forks/create.ts +++ b/src/lib/forks/create.ts @@ -2,13 +2,15 @@ import { getStorageConfig } from '@auth/provider.js'; import { createBucket } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('forks', 'create'); export default async function create(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const forkName = getOption(options, ['fork-name', 'forkName']); const snapshot = getOption(options, ['snapshot', 's', 'S']); @@ -31,5 +33,11 @@ export default async function create(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log( + JSON.stringify({ action: 'created', name: forkName, forkOf: name }) + ); + } + printSuccess(context, { name, forkName }); } diff --git a/src/lib/forks/list.ts b/src/lib/forks/list.ts index 0a0910c..5dee405 100644 --- a/src/lib/forks/list.ts +++ b/src/lib/forks/list.ts @@ -3,7 +3,7 @@ import { getBucketInfo, listBuckets } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('forks', 'list'); @@ -11,10 +11,7 @@ export default async function list(options: Record) { printStart(context); const name = getOption(options, ['name']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (!name) { failWithError(context, 'Source bucket name is required'); diff --git a/src/lib/iam/policies/create.ts b/src/lib/iam/policies/create.ts index 0b2be47..15bda0b 100644 --- a/src/lib/iam/policies/create.ts +++ b/src/lib/iam/policies/create.ts @@ -4,7 +4,7 @@ import { getOAuthIAMConfig } from '@auth/iam.js'; import { addPolicy, type PolicyDocument } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseDocument, readStdin } from './utils.js'; @@ -13,6 +13,8 @@ const context = msg('iam policies', 'create'); export default async function create(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const documentArg = getOption(options, ['document', 'd']); const description = getOption(options, ['description']) ?? ''; @@ -71,6 +73,12 @@ export default async function create(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log( + JSON.stringify({ action: 'created', name: data.name, arn: data.resource }) + ); + } + printSuccess(context, { name: data.name }); console.log(`Resource: ${data.resource}`); } diff --git a/src/lib/iam/policies/delete.ts b/src/lib/iam/policies/delete.ts index a022c26..bcc2de8 100644 --- a/src/lib/iam/policies/delete.ts +++ b/src/lib/iam/policies/delete.ts @@ -5,15 +5,17 @@ import { deletePolicy, listPolicies } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam policies', 'delete'); export default async function del(options: Record) { printStart(context); + const format = getFormat(options); + let resource = getOption(options, ['resource']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); const iamConfig = await getOAuthIAMConfig(context); @@ -64,5 +66,9 @@ export default async function del(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'deleted', arn: resource })); + } + printSuccess(context, { resource }); } diff --git a/src/lib/iam/policies/edit.ts b/src/lib/iam/policies/edit.ts index 14f4e3d..e8f721b 100644 --- a/src/lib/iam/policies/edit.ts +++ b/src/lib/iam/policies/edit.ts @@ -12,7 +12,7 @@ import { import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseDocument, readStdin } from './utils.js'; @@ -21,6 +21,8 @@ const context = msg('iam policies', 'edit'); export default async function edit(options: Record) { printStart(context); + const format = getFormat(options); + let resource = getOption(options, ['resource']); const documentArg = getOption(options, ['document', 'd']); const description = getOption(options, ['description']); @@ -115,5 +117,9 @@ export default async function edit(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', arn: data.resource })); + } + printSuccess(context, { resource: data.resource }); } diff --git a/src/lib/iam/policies/get.ts b/src/lib/iam/policies/get.ts index 17c44e4..ca27996 100644 --- a/src/lib/iam/policies/get.ts +++ b/src/lib/iam/policies/get.ts @@ -5,7 +5,7 @@ import { getPolicy, listPolicies } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam policies', 'get'); @@ -13,10 +13,7 @@ export default async function get(options: Record) { printStart(context); let resource = getOption(options, ['resource']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const iamConfig = await getOAuthIAMConfig(context); diff --git a/src/lib/iam/policies/list.ts b/src/lib/iam/policies/list.ts index dc3992b..919cbe0 100644 --- a/src/lib/iam/policies/list.ts +++ b/src/lib/iam/policies/list.ts @@ -3,17 +3,14 @@ import { listPolicies } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat } from '@utils/options.js'; const context = msg('iam policies', 'list'); export default async function list(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const iamConfig = await getOAuthIAMConfig(context); diff --git a/src/lib/iam/users/invite.ts b/src/lib/iam/users/invite.ts index e94b372..a161e66 100644 --- a/src/lib/iam/users/invite.ts +++ b/src/lib/iam/users/invite.ts @@ -2,13 +2,15 @@ import { getOAuthIAMConfig, isFlyOrganization } from '@auth/iam.js'; import { inviteUser } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam users', 'invite'); export default async function invite(options: Record) { printStart(context); + const format = getFormat(options); + if (isFlyOrganization()) return; const emailOption = getOption(options, ['email']); @@ -48,5 +50,11 @@ export default async function invite(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log( + JSON.stringify({ action: 'invited', email: emails.join(', ') }) + ); + } + printSuccess(context, { email: emails.join(', ') }); } diff --git a/src/lib/iam/users/list.ts b/src/lib/iam/users/list.ts index 7ecacad..6becb49 100644 --- a/src/lib/iam/users/list.ts +++ b/src/lib/iam/users/list.ts @@ -8,17 +8,14 @@ import { type TableColumn, } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat } from '@utils/options.js'; const context = msg('iam users', 'list'); export default async function list(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (isFlyOrganization()) return; diff --git a/src/lib/iam/users/remove.ts b/src/lib/iam/users/remove.ts index eb37f15..5503813 100644 --- a/src/lib/iam/users/remove.ts +++ b/src/lib/iam/users/remove.ts @@ -5,15 +5,17 @@ import { listUsers, removeUser as removeUserFromOrg } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam users', 'remove'); export default async function removeUser(options: Record) { printStart(context); + const format = getFormat(options); + const resourceOption = getOption(options, ['resource']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); if (isFlyOrganization()) return; @@ -72,5 +74,9 @@ export default async function removeUser(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'removed', users: resources })); + } + printSuccess(context); } diff --git a/src/lib/iam/users/revoke-invitation.ts b/src/lib/iam/users/revoke-invitation.ts index 15453f6..293a1d6 100644 --- a/src/lib/iam/users/revoke-invitation.ts +++ b/src/lib/iam/users/revoke-invitation.ts @@ -5,7 +5,7 @@ import { listUsers, revokeInvitation as revokeInv } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam users', 'revoke-invitation'); @@ -14,8 +14,10 @@ export default async function revokeInvitation( ) { printStart(context); + const format = getFormat(options); + const resourceOption = getOption(options, ['resource']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); if (isFlyOrganization()) return; @@ -77,5 +79,9 @@ export default async function revokeInvitation( failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'revoked', invitations: resources })); + } + printSuccess(context); } diff --git a/src/lib/iam/users/update-role.ts b/src/lib/iam/users/update-role.ts index 6def3b9..ec076ca 100644 --- a/src/lib/iam/users/update-role.ts +++ b/src/lib/iam/users/update-role.ts @@ -5,13 +5,15 @@ import { listUsers, updateUserRole } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('iam users', 'update-role'); export default async function updateRole(options: Record) { printStart(context); + const format = getFormat(options); + if (isFlyOrganization()) return; const validRoles = ['admin', 'member'] as const; @@ -98,5 +100,9 @@ export default async function updateRole(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log(JSON.stringify({ action: 'updated', users: roleUpdates })); + } + printSuccess(context); } diff --git a/src/lib/login/credentials.ts b/src/lib/login/credentials.ts index 3ab11c1..38ea31f 100644 --- a/src/lib/login/credentials.ts +++ b/src/lib/login/credentials.ts @@ -11,6 +11,7 @@ import { whoami } from '@tigrisdata/iam'; import { failWithError, printNextActions } from '@utils/exit.js'; import { requireInteractive } from '@utils/interactive.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; +import { getFormat } from '@utils/options.js'; import { DEFAULT_STORAGE_ENDPOINT } from '../../constants.js'; @@ -24,6 +25,8 @@ const context = msg('login', 'credentials'); export default async function credentials(options: Record) { printStart(context); + const format = getFormat(options); + let accessKey = options['access-key'] || options['accessKey'] || @@ -105,6 +108,10 @@ export default async function credentials(options: Record) { // Non-fatal — org will just be missing } + if (format === 'json') { + console.log(JSON.stringify({ action: 'logged_in' })); + } + printSuccess(context); printNextActions(context); } diff --git a/src/lib/login/oauth.ts b/src/lib/login/oauth.ts index 0b0424f..acd7719 100644 --- a/src/lib/login/oauth.ts +++ b/src/lib/login/oauth.ts @@ -9,11 +9,16 @@ import { printStart, printSuccess, } from '@utils/messages.js'; +import { getFormat } from '@utils/options.js'; const context = msg('login', 'oauth'); -export async function oauth(): Promise { +export async function oauth( + options: Record = {} +): Promise { printStart(context); + + const format = getFormat(options); try { const authClient = getAuthClient(); @@ -38,6 +43,11 @@ export async function oauth(): Promise { if (orgs.length > 0) { const firstOrg = orgs[0]; await storeSelectedOrganization(firstOrg.id); + + if (format === 'json') { + console.log(JSON.stringify({ action: 'logged_in' })); + } + printSuccess(context, { org: firstOrg.displayName || firstOrg.name }); printNextActions(context); @@ -45,6 +55,10 @@ export async function oauth(): Promise { printHint(context, { count: orgs.length }); } } else { + if (format === 'json') { + console.log(JSON.stringify({ action: 'logged_in' })); + } + printSuccess(context, { org: 'none' }); printNextActions(context); } diff --git a/src/lib/logout.ts b/src/lib/logout.ts index 14e8738..12228a9 100644 --- a/src/lib/logout.ts +++ b/src/lib/logout.ts @@ -1,15 +1,25 @@ import { clearAllData } from '@auth/storage.js'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; +import { getFormat } from '@utils/options.js'; const context = msg('logout'); -export default async function logout(): Promise { +export default async function logout( + options: Record = {} +): Promise { printStart(context); + + const format = getFormat(options); + try { // Clear all authentication data await clearAllData(); + if (format === 'json') { + console.log(JSON.stringify({ action: 'logged_out' })); + } + printSuccess(context); } catch (error) { failWithError(context, error); diff --git a/src/lib/ls.ts b/src/lib/ls.ts index 4b97500..5451d8d 100644 --- a/src/lib/ls.ts +++ b/src/lib/ls.ts @@ -2,7 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { list, listBuckets } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; export default async function ls(options: Record) { @@ -12,10 +12,7 @@ export default async function ls(options: Record) { 'snapshotVersion', 'snapshot', ]); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (!pathString) { // No path provided, list all buckets diff --git a/src/lib/mk.ts b/src/lib/mk.ts index 266e19d..1dc45b4 100644 --- a/src/lib/mk.ts +++ b/src/lib/mk.ts @@ -2,7 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { createBucket, put, type StorageClass } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; import { parseLocations } from '@utils/locations.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; export default async function mk(options: Record) { @@ -19,10 +19,7 @@ export default async function mk(options: Record) { } const config = await getStorageConfig(); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (!path) { // Create a bucket diff --git a/src/lib/mv.ts b/src/lib/mv.ts index 9de8ef3..3be9b63 100644 --- a/src/lib/mv.ts +++ b/src/lib/mv.ts @@ -3,7 +3,7 @@ import { get, head, list, put, remove } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; import { formatSize } from '@utils/format.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { globToRegex, isPathFolder, @@ -19,12 +19,9 @@ let _jsonMode = false; export default async function mv(options: Record) { const src = getOption(options, ['src']); const dest = getOption(options, ['dest']); - const force = getOption(options, ['force', 'f', 'F', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); const recursive = !!getOption(options, ['recursive', 'r']); - const jsonFlag = getOption(options, ['json']); - const format = jsonFlag - ? 'json' - : getOption(options, ['format'], 'table'); + const format = getFormat(options); _jsonMode = format === 'json'; if (!src || !dest) { diff --git a/src/lib/objects/delete.ts b/src/lib/objects/delete.ts index 7a3c431..ef70d84 100644 --- a/src/lib/objects/delete.ts +++ b/src/lib/objects/delete.ts @@ -13,21 +13,18 @@ import { printStart, printSuccess, } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('objects', 'delete'); export default async function deleteObject(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const bucket = getOption(options, ['bucket']); const keys = getOption(options, ['key']); - const force = getOption(options, ['force', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); if (!bucket) { failWithError(context, 'Bucket name is required'); diff --git a/src/lib/objects/get.ts b/src/lib/objects/get.ts index 55086e0..a3fc8f4 100644 --- a/src/lib/objects/get.ts +++ b/src/lib/objects/get.ts @@ -2,7 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { get } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { createWriteStream, writeFileSync } from 'fs'; import { extname } from 'path'; import { Readable } from 'stream'; @@ -104,10 +104,7 @@ function detectFormat(key: string, output?: string): 'string' | 'stream' { export default async function getObject(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const outputFormat = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const outputFormat = getFormat(options); const bucket = getOption(options, ['bucket']); const key = getOption(options, ['key']); diff --git a/src/lib/objects/list.ts b/src/lib/objects/list.ts index 3502995..c9a6345 100644 --- a/src/lib/objects/list.ts +++ b/src/lib/objects/list.ts @@ -3,7 +3,7 @@ import { list } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('objects', 'list'); @@ -12,10 +12,7 @@ export default async function listObjects(options: Record) { const bucket = getOption(options, ['bucket']); const prefix = getOption(options, ['prefix', 'p', 'P']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const snapshotVersion = getOption(options, [ 'snapshot-version', 'snapshotVersion', diff --git a/src/lib/objects/put.ts b/src/lib/objects/put.ts index a30f265..4e9d6ae 100644 --- a/src/lib/objects/put.ts +++ b/src/lib/objects/put.ts @@ -3,7 +3,7 @@ import { put } from '@tigrisdata/storage'; import { failWithError, printNextActions } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { calculateUploadParams } from '@utils/upload.js'; import { createReadStream, statSync } from 'fs'; import { Readable } from 'stream'; @@ -23,10 +23,7 @@ export default async function putObject(options: Record) { 't', 'T', ]); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (!bucket) { failWithError(context, 'Bucket name is required'); diff --git a/src/lib/objects/set.ts b/src/lib/objects/set.ts index 86d6c31..b4d4466 100644 --- a/src/lib/objects/set.ts +++ b/src/lib/objects/set.ts @@ -2,17 +2,14 @@ import { getStorageConfig } from '@auth/provider.js'; import { updateObject } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('objects', 'set'); export default async function setObject(options: Record) { printStart(context); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const bucket = getOption(options, ['bucket']); const key = getOption(options, ['key']); diff --git a/src/lib/organizations/create.ts b/src/lib/organizations/create.ts index ce24cf7..b06523f 100644 --- a/src/lib/organizations/create.ts +++ b/src/lib/organizations/create.ts @@ -4,7 +4,7 @@ import { getSelectedOrganization } from '@auth/storage.js'; import { createOrganization } from '@tigrisdata/iam'; import { failWithError, printNextActions } from '@utils/exit.js'; import { msg, printHint, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('organizations', 'create'); @@ -32,6 +32,8 @@ export default async function create(options: Record) { const config = await getStorageConfig(); + const format = getFormat(options); + const { data, error } = await createOrganization(name, { config }); if (error) { @@ -40,6 +42,10 @@ export default async function create(options: Record) { const id = data.id; + if (format === 'json') { + console.log(JSON.stringify({ action: 'created', name, id })); + } + printSuccess(context, { name, id }); printHint(context, { name }); printNextActions(context, { name }); diff --git a/src/lib/organizations/list.ts b/src/lib/organizations/list.ts index a9e3f78..beca0a9 100644 --- a/src/lib/organizations/list.ts +++ b/src/lib/organizations/list.ts @@ -10,7 +10,7 @@ import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { requireInteractive } from '@utils/interactive.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat } from '@utils/options.js'; import Enquirer from 'enquirer'; const context = msg('organizations', 'list'); @@ -20,10 +20,7 @@ export default async function list(options: Record) { if (requireOAuthLogin('Organization listing and selection')) return; - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'select'); + const format = getFormat(options, 'select'); // For Fly users, fetch organizations from userinfo endpoint const authClient = getAuthClient(); diff --git a/src/lib/organizations/select.ts b/src/lib/organizations/select.ts index 4632a05..3bfdb48 100644 --- a/src/lib/organizations/select.ts +++ b/src/lib/organizations/select.ts @@ -8,7 +8,7 @@ import { printStart, printSuccess, } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('organizations', 'select'); @@ -17,6 +17,8 @@ export default async function select(options: Record) { if (requireOAuthLogin('Organization selection')) return; + const format = getFormat(options); + const name = getOption(options, ['name', 'N']); if (!name) { @@ -50,6 +52,10 @@ export default async function select(options: Record) { // Store selected organization await storeSelectedOrganization(org.id); + if (format === 'json') { + console.log(JSON.stringify({ action: 'selected', organization: org.name })); + } + printSuccess(context, { name: org.name }); printNextActions(context, { name: org.name }); } diff --git a/src/lib/presign.ts b/src/lib/presign.ts index 2dfa0ac..fcea32a 100644 --- a/src/lib/presign.ts +++ b/src/lib/presign.ts @@ -7,7 +7,7 @@ import { listAccessKeys } from '@tigrisdata/iam'; import { getPresignedUrl } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; import { formatJson } from '@utils/format.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; import enquirer from 'enquirer'; const { prompt } = enquirer; @@ -34,10 +34,7 @@ export default async function presign(options: Record) { getOption(options, ['expires-in', 'expiresIn', 'e']) ?? '3600', 10 ); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : (getOption(options, ['format', 'f']) ?? 'url'); + const format = getFormat(options, 'url'); const accessKeyFlag = getOption(options, ['access-key', 'accessKey']); const config = await getStorageConfig(); diff --git a/src/lib/rm.ts b/src/lib/rm.ts index 03a0b74..4c17f81 100644 --- a/src/lib/rm.ts +++ b/src/lib/rm.ts @@ -2,7 +2,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { list, remove, removeBucket } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; import { confirm, requireInteractive } from '@utils/interactive.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { globToRegex, isPathFolder, @@ -16,12 +16,9 @@ let _jsonMode = false; export default async function rm(options: Record) { const pathString = getOption(options, ['path']); - const force = getOption(options, ['force', 'f', 'F', 'yes', 'y']); + const force = getOption(options, ['yes', 'y']); const recursive = !!getOption(options, ['recursive', 'r']); - const jsonFlag = getOption(options, ['json']); - const format = jsonFlag - ? 'json' - : getOption(options, ['format'], 'table'); + const format = getFormat(options); _jsonMode = format === 'json'; if (!pathString) { diff --git a/src/lib/snapshots/list.ts b/src/lib/snapshots/list.ts index 289d22b..4875ef6 100644 --- a/src/lib/snapshots/list.ts +++ b/src/lib/snapshots/list.ts @@ -3,7 +3,7 @@ import { listBucketSnapshots } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { formatOutput } from '@utils/format.js'; import { msg, printEmpty, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('snapshots', 'list'); @@ -11,10 +11,7 @@ export default async function list(options: Record) { printStart(context); const name = getOption(options, ['name']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); if (!name) { failWithError(context, 'Bucket name is required'); diff --git a/src/lib/snapshots/take.ts b/src/lib/snapshots/take.ts index 6ad7af3..5327f54 100644 --- a/src/lib/snapshots/take.ts +++ b/src/lib/snapshots/take.ts @@ -2,13 +2,15 @@ import { getStorageConfig } from '@auth/provider.js'; import { createBucketSnapshot } from '@tigrisdata/storage'; import { failWithError } from '@utils/exit.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; const context = msg('snapshots', 'take'); export default async function take(options: Record) { printStart(context); + const format = getFormat(options); + const name = getOption(options, ['name']); const snapshotName = getOption(options, [ 'snapshot-name', @@ -30,6 +32,16 @@ export default async function take(options: Record) { failWithError(context, error); } + if (format === 'json') { + console.log( + JSON.stringify({ + action: 'taken', + bucket: name, + version: data?.snapshotVersion, + }) + ); + } + printSuccess(context, { name, snapshotName: snapshotName || data?.snapshotVersion, diff --git a/src/lib/stat.ts b/src/lib/stat.ts index 51951ee..4483a72 100644 --- a/src/lib/stat.ts +++ b/src/lib/stat.ts @@ -4,7 +4,7 @@ import { buildBucketInfo } from '@utils/bucket-info.js'; import { failWithError } from '@utils/exit.js'; import { formatOutput, formatSize } from '@utils/format.js'; import { msg, printStart, printSuccess } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; const context = msg('stat'); @@ -13,10 +13,7 @@ export default async function stat(options: Record) { printStart(context); const pathString = getOption(options, ['path']); - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const snapshotVersion = getOption(options, [ 'snapshot-version', 'snapshotVersion', diff --git a/src/lib/touch.ts b/src/lib/touch.ts index eb8f557..45011dc 100644 --- a/src/lib/touch.ts +++ b/src/lib/touch.ts @@ -1,7 +1,7 @@ import { getStorageConfig } from '@auth/provider.js'; import { put } from '@tigrisdata/storage'; import { exitWithError } from '@utils/exit.js'; -import { getOption } from '@utils/options.js'; +import { getFormat, getOption } from '@utils/options.js'; import { parseAnyPath } from '@utils/path.js'; export default async function touch(options: Record) { @@ -21,10 +21,7 @@ export default async function touch(options: Record) { exitWithError('Object key is required (use mk to create buckets)'); } - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const config = await getStorageConfig(); diff --git a/src/lib/whoami.ts b/src/lib/whoami.ts index 01f234f..4f6aa7f 100644 --- a/src/lib/whoami.ts +++ b/src/lib/whoami.ts @@ -8,7 +8,7 @@ import { import { listOrganizations } from '@tigrisdata/iam'; import { failWithError } from '@utils/exit.js'; import { msg, printAlreadyDone } from '@utils/messages.js'; -import { getOption } from '@utils/options.js'; +import { getFormat } from '@utils/options.js'; const context = msg('whoami'); @@ -16,10 +16,7 @@ export default async function whoami( options: Record = {} ): Promise { try { - const json = getOption(options, ['json']); - const format = json - ? 'json' - : getOption(options, ['format', 'f', 'F'], 'table'); + const format = getFormat(options); const loginMethod = getLoginMethod(); const credentials = getCredentials(); diff --git a/src/specs.yaml b/src/specs.yaml index 8eee053..6adfd49 100644 --- a/src/specs.yaml +++ b/src/specs.yaml @@ -86,6 +86,20 @@ definitions: access_options: &access_options [public, private] + global_arguments: &global_arguments + - name: format + description: Output format + alias: f + options: [json, table] + default: table + - name: json + description: Output as JSON + type: flag + - name: yes + description: Skip confirmation prompts + type: flag + alias: "y" + commands: ######################### # Authentication @@ -202,16 +216,6 @@ commands: onSuccess: '' onFailure: 'Failed to get user information' onAlreadyDone: "Not authenticated\nRun \"tigris login\" to authenticate" - arguments: - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag - # logout - name: logout description: End the current session and clear login state. Credentials saved via 'configure' are kept @@ -245,14 +249,6 @@ commands: description: Bucket name to test access against (optional) alias: b required: false - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag ######################### @@ -287,9 +283,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag # mk - name: mk @@ -353,14 +346,6 @@ commands: - name: source-snapshot description: Fork from a specific snapshot of the source bucket. Accepts a snapshot version string or any UNIX nanosecond-precision timestamp (e.g. 1765889000501544464). Requires --fork-of alias: source-snap - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # touch - name: touch @@ -378,14 +363,6 @@ commands: examples: - my-bucket/my-file.txt - t3://my-bucket/my-file.txt - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # stat - name: stat @@ -412,9 +389,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: snapshot-version description: Read from a specific bucket snapshot. Accepts a snapshot version string or any UNIX nanosecond-precision timestamp (e.g. 1765889000501544464) alias: snapshot @@ -455,9 +429,6 @@ commands: alias: f options: [url, json] default: url - - name: json - description: Output as JSON - type: flag # cp - name: cp @@ -494,14 +465,6 @@ commands: type: flag alias: r description: Copy directories recursively - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # mv - name: mv @@ -533,17 +496,6 @@ commands: type: flag alias: r description: Move directories recursively - - name: force - type: flag - alias: f - description: Skip confirmation prompt - - name: format - description: Output format - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # rm - name: rm @@ -570,17 +522,6 @@ commands: type: flag alias: r description: Remove directories recursively - - name: force - type: flag - alias: f - description: Skip confirmation prompt - - name: format - description: Output format - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag ######################### # Manage organizations @@ -611,9 +552,6 @@ commands: alias: f options: [json, table, xml, select] default: select - - name: json - description: Output as JSON - type: flag - name: select description: Interactive selection mode alias: i @@ -691,9 +629,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: forks-of description: Only list buckets that are forks of the named source bucket # create @@ -761,14 +696,6 @@ commands: - name: source-snapshot description: Fork from a specific snapshot of the source bucket. Accepts a snapshot version string or any UNIX nanosecond-precision timestamp (e.g. 1765889000501544464). Requires --fork-of alias: source-snap - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # get - name: get description: Show details for a bucket including access level, region, tier, and custom domain @@ -791,9 +718,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag # delete - name: delete description: Delete one or more buckets by name. The bucket must be empty or delete-protection must be off @@ -816,17 +740,6 @@ commands: multiple: true examples: - my-bucket - - name: force - type: flag - description: Skip confirmation prompt - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # set - name: set description: Update settings on an existing bucket such as access level, location, caching, or custom domain @@ -874,13 +787,6 @@ commands: - name: enable-additional-headers description: Enable additional HTTP headers (X-Content-Type-Options nosniff) type: boolean - - name: format - description: Output format - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # set-ttl - name: set-ttl description: Configure object expiration (TTL) on a bucket. Objects expire after a number of days or on a specific date @@ -1124,9 +1030,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag # create - name: create description: (Deprecated, use "buckets create --fork-of") Create a new fork (copy-on-write clone) of the source bucket @@ -1191,9 +1094,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag # take - name: take description: Take a new snapshot of the bucket's current state. Optionally provide a name for the snapshot @@ -1259,9 +1159,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: snapshot-version description: Read from a specific bucket snapshot. Accepts a snapshot version string or any UNIX nanosecond-precision timestamp (e.g. 1765889000501544464) alias: snapshot @@ -1299,14 +1196,6 @@ commands: - name: snapshot-version description: Read from a specific bucket snapshot. Accepts a snapshot version string or any UNIX nanosecond-precision timestamp (e.g. 1765889000501544464) alias: snapshot - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # put - name: put description: Upload a local file as an object. Content-type is auto-detected from extension unless overridden @@ -1352,9 +1241,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag # delete - name: delete description: Delete one or more objects by key from the given bucket @@ -1383,17 +1269,6 @@ commands: multiple: true examples: - my-file.txt - - name: force - type: flag - description: Skip confirmation prompt - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag # set - name: set description: Update settings on an existing object such as access level @@ -1422,14 +1297,6 @@ commands: - name: new-key description: Rename the object to a new key alias: n - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag ######################### # Manage access keys @@ -1458,9 +1325,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: create description: Create a new access key with the given name. Returns the key ID and secret (shown only once) alias: c @@ -1480,14 +1344,6 @@ commands: required: true examples: - my-key - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag - name: delete description: Permanently delete an access key by its ID. This revokes all access immediately alias: d @@ -1504,17 +1360,6 @@ commands: required: true examples: - tid_AaBbCcDdEeFf - - name: force - type: flag - description: Skip confirmation prompt - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag - name: get description: Show details for an access key including its name, creation date, and assigned bucket roles alias: g @@ -1531,14 +1376,6 @@ commands: required: true examples: - tid_AaBbCcDdEeFf - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag - name: assign description: Assign per-bucket roles to an access key. Pair each --bucket with a --role (Editor or ReadOnly), or use --admin for org-wide access alias: a @@ -1578,14 +1415,6 @@ commands: - name: revoke-roles description: Revoke all bucket roles from the access key type: flag - - name: format - description: Output format - alias: f - options: [json, table] - default: table - - name: json - description: Output as JSON - type: flag ######################### # IAM - Identity and Access Management @@ -1621,9 +1450,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: get description: Show details for a policy including its document and attached users. If no ARN provided, shows interactive selection alias: g @@ -1647,9 +1473,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: create description: Create a new policy with the given name and policy document. Document can be provided via file, inline JSON, or stdin alias: c @@ -1718,9 +1541,6 @@ commands: required: false examples: - arn:aws:iam::org_id:policy/my-policy - - name: force - type: flag - description: Skip confirmation prompt - name: users description: Manage organization users and invitations @@ -1747,9 +1567,6 @@ commands: alias: f options: [json, table, xml] default: table - - name: json - description: Output as JSON - type: flag - name: invite description: Invite users to the organization by email alias: i @@ -1793,9 +1610,6 @@ commands: type: positional required: false multiple: true - - name: force - type: flag - description: Skip confirmation prompt - name: update-role description: Update user roles in the organization. If no user ID provided, shows interactive selection alias: ur @@ -1839,6 +1653,3 @@ commands: type: positional required: false multiple: true - - name: force - type: flag - description: Skip confirmation prompt diff --git a/src/types.ts b/src/types.ts index f3ef84d..68fd9e6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,6 +50,10 @@ export interface Specs { name: string; description: string; version: string; + definitions?: { + global_arguments?: Argument[]; + [key: string]: unknown; + }; commands: CommandSpec[]; } diff --git a/src/utils/options.ts b/src/utils/options.ts index cbac8af..b5434da 100644 --- a/src/utils/options.ts +++ b/src/utils/options.ts @@ -18,6 +18,22 @@ export function getOption( return defaultValue; } +/** + * Resolve the output format from --json flag and --format option. + * --json takes precedence; falls back to --format or the given default. + */ +export function getFormat( + options: Record, + defaultFormat = 'table' +): string { + const json = getOption(options, ['json']); + if (json) return 'json'; + return ( + getOption(options, ['format', 'f', 'F'], defaultFormat) ?? + defaultFormat + ); +} + /** * Parses a boolean value from string or boolean input * - undefined → undefined