diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7562347..215b2b82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,10 +25,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "20" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0a91f735..53e0f5a5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,7 +19,7 @@ jobs: CLI_BUN_VERSION: '1.3.11' HOMEBREW_TAP_REPO: appwrite/homebrew-appwrite steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: token: ${{ secrets.GH_TOKEN }} - uses: oven-sh/setup-bun@v2 @@ -50,7 +50,7 @@ jobs: bun run windows-arm64 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24.14.1' registry-url: 'https://registry.npmjs.org' @@ -77,7 +77,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - name: Check out Homebrew tap - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: ${{ env.HOMEBREW_TAP_REPO }} token: ${{ secrets.HOMEBREW_TAP_GH_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c039e3c6..cce13660 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## 20.0.0 + +* Breaking: Removed `projects delete`. Use `project delete` instead +* Breaking: Replaced `projects update-mock-numbers` with `project create-mock-phone`, `list-mock-phones`, `get-mock-phone`, `update-mock-phone`, and `delete-mock-phone` +* Breaking: Renamed `projects update-auth-status` to `project update-auth-method` with `--method-id` and `--enabled` flags +* Breaking: Replaced `projects update-o-auth-2` with per-provider `project update-o-auth-2-` commands +* Breaking: Replaced `projects create-jwt` with `project create-ephemeral-key` for short-lived API keys +* Added `project list-o-auth-2-providers`, `get-o-auth-2-provider`, `list-policies`, `get-policy`, and `list-email-templates` commands +* Added `fusionauth`, `keycloak`, and `kick` to supported OAuth2 providers in `account` session commands +* Added `includes` field in `appwrite.config.json` to split resource arrays into separate JSON files; `init`, `pull`, and `push` resolve resource paths relative to each include + ## 19.2.0 * Added `completion install` command with completion scripts for zsh, bash, and fish diff --git a/README.md b/README.md index b0dae288..78cd327a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Appwrite Command Line SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-cli.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.9.2-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-1.9.3-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using ```sh $ appwrite -v -19.2.0 +20.0.0 ``` ### Install using prebuilt binaries @@ -83,7 +83,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc Once the installation completes, you can verify your install using ``` $ appwrite -v -19.2.0 +20.0.0 ``` ## Getting Started @@ -231,6 +231,45 @@ appwrite client --endpoint http://localhost/v1 --projectId --key =4.8.4 <6.1.0" } }, "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.1", "@typescript-eslint/type-utils": "8.59.1", "@typescript-eslint/utils": "8.59.1", "@typescript-eslint/visitor-keys": "8.59.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg=="], + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.1", "@typescript-eslint/types": "8.59.1", "@typescript-eslint/typescript-estree": "8.59.1", "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.0", "@typescript-eslint/types": "^8.59.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.1", "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg=="], - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0" } }, "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg=="], + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.1", "", { "dependencies": { "@typescript-eslint/types": "8.59.1", "@typescript-eslint/visitor-keys": "8.59.1" } }, "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0", "@typescript-eslint/utils": "8.59.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.1", "", { "dependencies": { "@typescript-eslint/types": "8.59.1", "@typescript-eslint/typescript-estree": "8.59.1", "@typescript-eslint/utils": "8.59.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w=="], - "@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "@typescript-eslint/types": ["@typescript-eslint/types@8.59.1", "", {}, "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A=="], - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.0", "@typescript-eslint/tsconfig-utils": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw=="], + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.1", "@typescript-eslint/tsconfig-utils": "8.59.1", "@typescript-eslint/types": "8.59.1", "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g=="], - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g=="], + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.1", "@typescript-eslint/types": "8.59.1", "@typescript-eslint/typescript-estree": "8.59.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q=="], + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.1", "", { "dependencies": { "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.9.10", "", {}, "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw=="], @@ -327,7 +327,7 @@ "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="], - "b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="], + "b4a": ["b4a@1.8.1", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw=="], "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -335,11 +335,11 @@ "bare-fs": ["bare-fs@4.7.1", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw=="], - "bare-os": ["bare-os@3.9.0", "", {}, "sha512-JTjuZyNIDpw+GytMO4a6TK1VXdVKKJr6DRxEHasyuYyShV2deuiHJK/ahGZlebc+SG0/wJCB9XK8gprBGDFi/Q=="], + "bare-os": ["bare-os@3.9.1", "", {}, "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ=="], "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], - "bare-stream": ["bare-stream@2.13.0", "", { "dependencies": { "streamx": "^2.25.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-abort-controller": "*", "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-abort-controller", "bare-buffer"] }, "sha512-3zAJRZMDFGjdn+RVnNpF9kuELw+0Fl3lpndM4NcEOhb9zwtSo/deETfuIwMSE5BXanA0FrN1qVjffGwAg2Y7EA=="], + "bare-stream": ["bare-stream@2.13.1", "", { "dependencies": { "streamx": "^2.25.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-abort-controller": "*", "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-abort-controller", "bare-buffer"] }, "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow=="], "bare-url": ["bare-url@2.4.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-/9a2j4ac6ckpmAHvod/ob7x439OAHst/drc2Clnq+reRYd/ovddwcF4LfoxHyNk5AuGBnPg+HqFjmE/Zpq6v0A=="], @@ -665,7 +665,7 @@ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], - "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], + "node-abi": ["node-abi@3.90.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-pZNQT7UnYlMwMBy5N1lV5X/YLTbZM5ncytN3xL7CHEzhDN8uVe0u55yaPUJICIJjaCW8NrM5BFdqr7HLweStNA=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], @@ -715,7 +715,7 @@ "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": "bin/pixelmatch" }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="], - "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], + "plist": ["plist@3.1.1", "", { "dependencies": { "@xmldom/xmldom": "^0.9.10", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA=="], "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], @@ -817,7 +817,7 @@ "tar-fs": ["tar-fs@3.1.2", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw=="], - "tar-stream": ["tar-stream@3.1.8", "", { "dependencies": { "b4a": "^1.6.4", "bare-fs": "^4.5.5", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ=="], + "tar-stream": ["tar-stream@3.2.0", "", { "dependencies": { "b4a": "^1.6.4", "bare-fs": "^4.5.5", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg=="], "teex": ["teex@1.0.1", "", { "dependencies": { "streamx": "^2.12.5" } }, "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg=="], @@ -857,7 +857,7 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "typescript-eslint": ["typescript-eslint@8.59.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.0", "@typescript-eslint/parser": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0", "@typescript-eslint/utils": "8.59.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw=="], + "typescript-eslint": ["typescript-eslint@8.59.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.1", "@typescript-eslint/parser": "8.59.1", "@typescript-eslint/typescript-estree": "8.59.1", "@typescript-eslint/utils": "8.59.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ=="], "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], @@ -915,7 +915,7 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], diff --git a/docs/examples/project/create-ephemeral-key.md b/docs/examples/project/create-ephemeral-key.md new file mode 100644 index 00000000..d80508a3 --- /dev/null +++ b/docs/examples/project/create-ephemeral-key.md @@ -0,0 +1,5 @@ +```bash +appwrite project create-ephemeral-key \ + --scopes one two three \ + --duration 1 +``` diff --git a/docs/examples/project/create-mock-phone.md b/docs/examples/project/create-mock-phone.md new file mode 100644 index 00000000..bed52904 --- /dev/null +++ b/docs/examples/project/create-mock-phone.md @@ -0,0 +1,5 @@ +```bash +appwrite project create-mock-phone \ + --number +12065550100 \ + --otp +``` diff --git a/docs/examples/project/delete-mock-phone.md b/docs/examples/project/delete-mock-phone.md new file mode 100644 index 00000000..7e817c34 --- /dev/null +++ b/docs/examples/project/delete-mock-phone.md @@ -0,0 +1,4 @@ +```bash +appwrite project delete-mock-phone \ + --number +12065550100 +``` diff --git a/docs/examples/project/delete.md b/docs/examples/project/delete.md new file mode 100644 index 00000000..376dee03 --- /dev/null +++ b/docs/examples/project/delete.md @@ -0,0 +1,3 @@ +```bash +appwrite project delete +``` diff --git a/docs/examples/project/get-mock-phone.md b/docs/examples/project/get-mock-phone.md new file mode 100644 index 00000000..a4437830 --- /dev/null +++ b/docs/examples/project/get-mock-phone.md @@ -0,0 +1,4 @@ +```bash +appwrite project get-mock-phone \ + --number +12065550100 +``` diff --git a/docs/examples/project/get-o-auth-2-provider.md b/docs/examples/project/get-o-auth-2-provider.md new file mode 100644 index 00000000..ba47af11 --- /dev/null +++ b/docs/examples/project/get-o-auth-2-provider.md @@ -0,0 +1,4 @@ +```bash +appwrite project get-o-auth-2-provider \ + --provider +``` diff --git a/docs/examples/project/get-policy.md b/docs/examples/project/get-policy.md new file mode 100644 index 00000000..3ad50505 --- /dev/null +++ b/docs/examples/project/get-policy.md @@ -0,0 +1,4 @@ +```bash +appwrite project get-policy \ + --policy-id password-dictionary +``` diff --git a/docs/examples/project/list-email-templates.md b/docs/examples/project/list-email-templates.md new file mode 100644 index 00000000..ea26ea33 --- /dev/null +++ b/docs/examples/project/list-email-templates.md @@ -0,0 +1,4 @@ +```bash +appwrite project list-email-templates \ + --limit 25 +``` diff --git a/docs/examples/project/list-mock-phones.md b/docs/examples/project/list-mock-phones.md new file mode 100644 index 00000000..16a0b930 --- /dev/null +++ b/docs/examples/project/list-mock-phones.md @@ -0,0 +1,4 @@ +```bash +appwrite project list-mock-phones \ + --limit 25 +``` diff --git a/docs/examples/project/list-o-auth-2-providers.md b/docs/examples/project/list-o-auth-2-providers.md new file mode 100644 index 00000000..df16be41 --- /dev/null +++ b/docs/examples/project/list-o-auth-2-providers.md @@ -0,0 +1,3 @@ +```bash +appwrite project list-o-auth-2-providers +``` diff --git a/docs/examples/project/list-policies.md b/docs/examples/project/list-policies.md new file mode 100644 index 00000000..61aa6d4b --- /dev/null +++ b/docs/examples/project/list-policies.md @@ -0,0 +1,4 @@ +```bash +appwrite project list-policies \ + --limit 25 +``` diff --git a/docs/examples/project/update-auth-method.md b/docs/examples/project/update-auth-method.md new file mode 100644 index 00000000..45ca9a74 --- /dev/null +++ b/docs/examples/project/update-auth-method.md @@ -0,0 +1,5 @@ +```bash +appwrite project update-auth-method \ + --method-id email-password \ + --enabled false +``` diff --git a/docs/examples/project/update-mock-phone.md b/docs/examples/project/update-mock-phone.md new file mode 100644 index 00000000..f8b267d3 --- /dev/null +++ b/docs/examples/project/update-mock-phone.md @@ -0,0 +1,5 @@ +```bash +appwrite project update-mock-phone \ + --number +12065550100 \ + --otp +``` diff --git a/docs/examples/project/update-o-auth-2-amazon.md b/docs/examples/project/update-o-auth-2-amazon.md new file mode 100644 index 00000000..fd478a99 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-amazon.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-amazon +``` diff --git a/docs/examples/project/update-o-auth-2-apple.md b/docs/examples/project/update-o-auth-2-apple.md new file mode 100644 index 00000000..6b74296b --- /dev/null +++ b/docs/examples/project/update-o-auth-2-apple.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-apple +``` diff --git a/docs/examples/project/update-o-auth-2-auth-0.md b/docs/examples/project/update-o-auth-2-auth-0.md new file mode 100644 index 00000000..84fc7820 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-auth-0.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-auth-0 +``` diff --git a/docs/examples/project/update-o-auth-2-authentik.md b/docs/examples/project/update-o-auth-2-authentik.md new file mode 100644 index 00000000..9eaf8366 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-authentik.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-authentik +``` diff --git a/docs/examples/project/update-o-auth-2-autodesk.md b/docs/examples/project/update-o-auth-2-autodesk.md new file mode 100644 index 00000000..fb114655 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-autodesk.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-autodesk +``` diff --git a/docs/examples/project/update-o-auth-2-bitbucket.md b/docs/examples/project/update-o-auth-2-bitbucket.md new file mode 100644 index 00000000..685fa8d2 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-bitbucket.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-bitbucket +``` diff --git a/docs/examples/project/update-o-auth-2-bitly.md b/docs/examples/project/update-o-auth-2-bitly.md new file mode 100644 index 00000000..48426eb3 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-bitly.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-bitly +``` diff --git a/docs/examples/project/update-o-auth-2-box.md b/docs/examples/project/update-o-auth-2-box.md new file mode 100644 index 00000000..e9ad37c7 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-box.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-box +``` diff --git a/docs/examples/project/update-o-auth-2-dailymotion.md b/docs/examples/project/update-o-auth-2-dailymotion.md new file mode 100644 index 00000000..36897064 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-dailymotion.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-dailymotion +``` diff --git a/docs/examples/project/update-o-auth-2-discord.md b/docs/examples/project/update-o-auth-2-discord.md new file mode 100644 index 00000000..83362d11 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-discord.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-discord +``` diff --git a/docs/examples/project/update-o-auth-2-disqus.md b/docs/examples/project/update-o-auth-2-disqus.md new file mode 100644 index 00000000..d34026f3 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-disqus.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-disqus +``` diff --git a/docs/examples/project/update-o-auth-2-dropbox.md b/docs/examples/project/update-o-auth-2-dropbox.md new file mode 100644 index 00000000..d2c2ee97 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-dropbox.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-dropbox +``` diff --git a/docs/examples/project/update-o-auth-2-etsy.md b/docs/examples/project/update-o-auth-2-etsy.md new file mode 100644 index 00000000..c8ed2e1c --- /dev/null +++ b/docs/examples/project/update-o-auth-2-etsy.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-etsy +``` diff --git a/docs/examples/project/update-o-auth-2-facebook.md b/docs/examples/project/update-o-auth-2-facebook.md new file mode 100644 index 00000000..a9d50ec4 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-facebook.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-facebook +``` diff --git a/docs/examples/project/update-o-auth-2-figma.md b/docs/examples/project/update-o-auth-2-figma.md new file mode 100644 index 00000000..a689ce49 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-figma.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-figma +``` diff --git a/docs/examples/project/update-o-auth-2-fusion-auth.md b/docs/examples/project/update-o-auth-2-fusion-auth.md new file mode 100644 index 00000000..f22e1e40 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-fusion-auth.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-fusion-auth +``` diff --git a/docs/examples/project/update-o-auth-2-git-hub.md b/docs/examples/project/update-o-auth-2-git-hub.md new file mode 100644 index 00000000..a3b08096 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-git-hub.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-git-hub +``` diff --git a/docs/examples/project/update-o-auth-2-gitlab.md b/docs/examples/project/update-o-auth-2-gitlab.md new file mode 100644 index 00000000..fd883f2c --- /dev/null +++ b/docs/examples/project/update-o-auth-2-gitlab.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-gitlab +``` diff --git a/docs/examples/project/update-o-auth-2-google.md b/docs/examples/project/update-o-auth-2-google.md new file mode 100644 index 00000000..aab34410 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-google.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-google +``` diff --git a/docs/examples/project/update-o-auth-2-keycloak.md b/docs/examples/project/update-o-auth-2-keycloak.md new file mode 100644 index 00000000..39ca172b --- /dev/null +++ b/docs/examples/project/update-o-auth-2-keycloak.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-keycloak +``` diff --git a/docs/examples/project/update-o-auth-2-kick.md b/docs/examples/project/update-o-auth-2-kick.md new file mode 100644 index 00000000..ed41a4c3 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-kick.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-kick +``` diff --git a/docs/examples/project/update-o-auth-2-linkedin.md b/docs/examples/project/update-o-auth-2-linkedin.md new file mode 100644 index 00000000..f2bfef2b --- /dev/null +++ b/docs/examples/project/update-o-auth-2-linkedin.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-linkedin +``` diff --git a/docs/examples/project/update-o-auth-2-microsoft.md b/docs/examples/project/update-o-auth-2-microsoft.md new file mode 100644 index 00000000..1eb97419 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-microsoft.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-microsoft +``` diff --git a/docs/examples/project/update-o-auth-2-notion.md b/docs/examples/project/update-o-auth-2-notion.md new file mode 100644 index 00000000..249e6bc0 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-notion.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-notion +``` diff --git a/docs/examples/project/update-o-auth-2-oidc.md b/docs/examples/project/update-o-auth-2-oidc.md new file mode 100644 index 00000000..70446b62 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-oidc.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-oidc +``` diff --git a/docs/examples/project/update-o-auth-2-okta.md b/docs/examples/project/update-o-auth-2-okta.md new file mode 100644 index 00000000..e98bb24b --- /dev/null +++ b/docs/examples/project/update-o-auth-2-okta.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-okta +``` diff --git a/docs/examples/project/update-o-auth-2-paypal-sandbox.md b/docs/examples/project/update-o-auth-2-paypal-sandbox.md new file mode 100644 index 00000000..56afb4c0 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-paypal-sandbox.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-paypal-sandbox +``` diff --git a/docs/examples/project/update-o-auth-2-paypal.md b/docs/examples/project/update-o-auth-2-paypal.md new file mode 100644 index 00000000..2a84b350 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-paypal.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-paypal +``` diff --git a/docs/examples/project/update-o-auth-2-podio.md b/docs/examples/project/update-o-auth-2-podio.md new file mode 100644 index 00000000..0e6ec3a8 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-podio.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-podio +``` diff --git a/docs/examples/project/update-o-auth-2-salesforce.md b/docs/examples/project/update-o-auth-2-salesforce.md new file mode 100644 index 00000000..b4832b0c --- /dev/null +++ b/docs/examples/project/update-o-auth-2-salesforce.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-salesforce +``` diff --git a/docs/examples/project/update-o-auth-2-slack.md b/docs/examples/project/update-o-auth-2-slack.md new file mode 100644 index 00000000..03985698 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-slack.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-slack +``` diff --git a/docs/examples/project/update-o-auth-2-spotify.md b/docs/examples/project/update-o-auth-2-spotify.md new file mode 100644 index 00000000..3d9d8efe --- /dev/null +++ b/docs/examples/project/update-o-auth-2-spotify.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-spotify +``` diff --git a/docs/examples/project/update-o-auth-2-stripe.md b/docs/examples/project/update-o-auth-2-stripe.md new file mode 100644 index 00000000..cc317881 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-stripe.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-stripe +``` diff --git a/docs/examples/project/update-o-auth-2-tradeshift-sandbox.md b/docs/examples/project/update-o-auth-2-tradeshift-sandbox.md new file mode 100644 index 00000000..cf13b34b --- /dev/null +++ b/docs/examples/project/update-o-auth-2-tradeshift-sandbox.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-tradeshift-sandbox +``` diff --git a/docs/examples/project/update-o-auth-2-tradeshift.md b/docs/examples/project/update-o-auth-2-tradeshift.md new file mode 100644 index 00000000..1bf3e889 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-tradeshift.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-tradeshift +``` diff --git a/docs/examples/project/update-o-auth-2-twitch.md b/docs/examples/project/update-o-auth-2-twitch.md new file mode 100644 index 00000000..cb4f970d --- /dev/null +++ b/docs/examples/project/update-o-auth-2-twitch.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-twitch +``` diff --git a/docs/examples/project/update-o-auth-2-word-press.md b/docs/examples/project/update-o-auth-2-word-press.md new file mode 100644 index 00000000..51db1383 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-word-press.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-word-press +``` diff --git a/docs/examples/project/update-o-auth-2-yahoo.md b/docs/examples/project/update-o-auth-2-yahoo.md new file mode 100644 index 00000000..1cac8450 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-yahoo.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-yahoo +``` diff --git a/docs/examples/project/update-o-auth-2-yandex.md b/docs/examples/project/update-o-auth-2-yandex.md new file mode 100644 index 00000000..8d367055 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-yandex.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-yandex +``` diff --git a/docs/examples/project/update-o-auth-2-zoho.md b/docs/examples/project/update-o-auth-2-zoho.md new file mode 100644 index 00000000..54dd7d07 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-zoho.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-zoho +``` diff --git a/docs/examples/project/update-o-auth-2-zoom.md b/docs/examples/project/update-o-auth-2-zoom.md new file mode 100644 index 00000000..ee5bbf74 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-zoom.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2-zoom +``` diff --git a/docs/examples/project/update-o-auth-2x.md b/docs/examples/project/update-o-auth-2x.md new file mode 100644 index 00000000..e1b988dd --- /dev/null +++ b/docs/examples/project/update-o-auth-2x.md @@ -0,0 +1,3 @@ +```bash +appwrite project update-o-auth-2x +``` diff --git a/docs/examples/projects/create-jwt.md b/docs/examples/projects/create-jwt.md deleted file mode 100644 index be167ed7..00000000 --- a/docs/examples/projects/create-jwt.md +++ /dev/null @@ -1,5 +0,0 @@ -```bash -appwrite projects create-jwt \ - --project-id \ - --scopes one two three -``` diff --git a/docs/examples/projects/delete.md b/docs/examples/projects/delete.md deleted file mode 100644 index d225400f..00000000 --- a/docs/examples/projects/delete.md +++ /dev/null @@ -1,4 +0,0 @@ -```bash -appwrite projects delete \ - --project-id -``` diff --git a/docs/examples/projects/update-auth-status.md b/docs/examples/projects/update-auth-status.md deleted file mode 100644 index 248750f7..00000000 --- a/docs/examples/projects/update-auth-status.md +++ /dev/null @@ -1,6 +0,0 @@ -```bash -appwrite projects update-auth-status \ - --project-id \ - --method email-password \ - --status false -``` diff --git a/docs/examples/projects/update-mock-numbers.md b/docs/examples/projects/update-mock-numbers.md deleted file mode 100644 index fb659a39..00000000 --- a/docs/examples/projects/update-mock-numbers.md +++ /dev/null @@ -1,5 +0,0 @@ -```bash -appwrite projects update-mock-numbers \ - --project-id \ - --numbers one two three -``` diff --git a/docs/examples/projects/update-o-auth-2.md b/docs/examples/projects/update-o-auth-2.md deleted file mode 100644 index 10d0adb0..00000000 --- a/docs/examples/projects/update-o-auth-2.md +++ /dev/null @@ -1,5 +0,0 @@ -```bash -appwrite projects update-o-auth-2 \ - --project-id \ - --provider amazon -``` diff --git a/index.ts b/index.ts index 65d57373..fb224dc6 100644 --- a/index.ts +++ b/index.ts @@ -12,6 +12,7 @@ export { Schema, Push, Pull }; export { Client } from "@appwrite.io/console"; export type { ConfigType, + ConfigIncludesType, SettingsType, FunctionType, SiteType, diff --git a/install.ps1 b/install.ps1 index b95d779a..1114464c 100644 --- a/install.ps1 +++ b/install.ps1 @@ -13,8 +13,8 @@ # You can use "View source" of this page to see the full script. # REPO -$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/19.2.0/appwrite-cli-win-x64.exe" -$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/19.2.0/appwrite-cli-win-arm64.exe" +$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.0.0/appwrite-cli-win-x64.exe" +$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/20.0.0/appwrite-cli-win-arm64.exe" $APPWRITE_BINARY_NAME = "appwrite.exe" diff --git a/install.sh b/install.sh index d5e9218d..f49f2dde 100644 --- a/install.sh +++ b/install.sh @@ -120,7 +120,7 @@ verifyMacOSCodeSignature() { downloadBinary() { echo "[2/5] Downloading executable for $OS ($ARCH) ..." - GITHUB_LATEST_VERSION="19.2.0" + GITHUB_LATEST_VERSION="20.0.0" GITHUB_FILE="appwrite-cli-${OS}-${ARCH}" GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE" diff --git a/lib/commands/config.ts b/lib/commands/config.ts index c161197f..b40bee64 100644 --- a/lib/commands/config.ts +++ b/lib/commands/config.ts @@ -5,6 +5,7 @@ import { validateContainerDuplicates, validateCrossDatabase, } from "./config-validations.js"; +import { CONFIG_RESOURCE_KEYS } from "../constants.js"; // ============================================================================ // Internal Helpers @@ -433,11 +434,58 @@ const BucketSchema = z // Config Schema // ============================================================================ +const ConfigIncludePathSchema = z + .string() + .trim() + .min(1, "Include path cannot be empty") + .refine((value) => !value.includes("\0"), { + message: "Include path cannot contain null bytes", + }) + .refine((value) => !value.includes("#"), { + message: "Include path cannot contain JSON pointer fragments", + }) + .refine((value) => !value.split(/[\\/]+/).includes(".."), { + message: "Include path cannot contain parent directory segments", + }) + .refine( + (value) => + !value.startsWith("/") && + !value.startsWith("\\") && + !/^[a-z]:[\\/]/i.test(value), + { + message: "Include path must be relative", + }, + ) + .refine((value) => !/^[a-z][a-z0-9+.-]*:/i.test(value), { + message: "Include path must be a local file path, not a URL", + }) + .refine((value) => value.toLowerCase().endsWith(".json"), { + message: "Include path must point to a JSON file", + }); + +type ConfigIncludePathShape = { + [K in (typeof CONFIG_RESOURCE_KEYS)[number]]: z.ZodOptional< + typeof ConfigIncludePathSchema + >; +}; + +const ConfigIncludesSchema = z + .object( + Object.fromEntries( + CONFIG_RESOURCE_KEYS.map((key) => [ + key, + ConfigIncludePathSchema.optional(), + ]), + ) as ConfigIncludePathShape, + ) + .strict(); + const ConfigSchema = z .object({ projectId: z.string(), projectName: z.string().optional(), endpoint: z.string().optional(), + includes: ConfigIncludesSchema.optional(), settings: z.lazy(() => SettingsSchema).optional(), functions: z.array(z.lazy(() => FunctionSchema)).optional(), sites: z.array(z.lazy(() => SiteSchema)).optional(), @@ -459,6 +507,7 @@ const ConfigSchema = z // ============================================================================ export type ConfigType = z.infer; +export type ConfigIncludesType = NonNullable; export type SettingsType = z.infer; export type SiteType = z.infer; export type FunctionType = z.infer; diff --git a/lib/commands/init.ts b/lib/commands/init.ts index d39ccd61..43231065 100644 --- a/lib/commands/init.ts +++ b/lib/commands/init.ts @@ -618,7 +618,11 @@ const initSkill = async (): Promise => { }; const initFunction = async (): Promise => { - process.chdir(localConfig.configDirectoryPath); + const configDirectory = localConfig.getResourceDirname("functions"); + if (!fs.existsSync(configDirectory)) { + fs.mkdirSync(configDirectory, { recursive: true }); + } + process.chdir(configDirectory); // TODO: Add CI/CD support (ID, name, runtime) const answers = await inquirer.prompt(questionsCreateFunction); @@ -788,7 +792,11 @@ const initFunction = async (): Promise => { }; const initSite = async (): Promise => { - process.chdir(localConfig.configDirectoryPath); + const configDirectory = localConfig.getResourceDirname("sites"); + if (!fs.existsSync(configDirectory)) { + fs.mkdirSync(configDirectory, { recursive: true }); + } + process.chdir(configDirectory); const answers = await inquirer.prompt(questionsCreateSite); const siteId = answers.id === "unique()" ? ID.unique() : answers.id; diff --git a/lib/commands/pull.ts b/lib/commands/pull.ts index f6179c2f..627ba835 100644 --- a/lib/commands/pull.ts +++ b/lib/commands/pull.ts @@ -71,6 +71,10 @@ export interface PullOptions { skipDeprecated?: boolean; withVariables?: boolean; noCode?: boolean; + resourceDirectories?: { + functions?: string; + sites?: string; + }; } interface PullFunctionsOptions { @@ -92,17 +96,22 @@ export interface PullSettingsResult { } async function createPullInstance( - options: { silent?: boolean; requiresConsoleAuth?: boolean } = { - silent: false, - requiresConsoleAuth: false, - }, + options: { + silent?: boolean; + requiresConsoleAuth?: boolean; + resource?: "functions" | "sites"; + } = {}, ): Promise { - const { silent, requiresConsoleAuth } = options; + const { silent = false, requiresConsoleAuth = false, resource } = options; const projectClient = await sdkForProject(); const consoleClient = await sdkForConsole(requiresConsoleAuth); const pullInstance = new Pull(projectClient, consoleClient, silent); - pullInstance.setConfigDirectoryPath(localConfig.configDirectoryPath); + pullInstance.setConfigDirectoryPath( + resource + ? localConfig.getResourceDirname(resource) + : localConfig.configDirectoryPath, + ); return pullInstance; } @@ -171,59 +180,72 @@ export class Pull { const updatedConfig: ConfigType = { ...config }; const shouldPullAll = options.all === true; + const originalConfigDirectoryPath = this.configDirectoryPath; - if (shouldPullAll || options.settings) { - const settings = await this.pullSettings(config.projectId); - updatedConfig.settings = settings.settings; - updatedConfig.projectName = settings.projectName; - } + try { + if (shouldPullAll || options.settings) { + const settings = await this.pullSettings(config.projectId); + updatedConfig.settings = settings.settings; + updatedConfig.projectName = settings.projectName; + } - if (shouldPullAll || options.functions) { - const functions = await this.pullFunctions({ - code: options.noCode === true ? false : true, - withVariables: options.withVariables, - }); - updatedConfig.functions = functions; - } + if (shouldPullAll || options.functions) { + if (options.resourceDirectories?.functions) { + this.setConfigDirectoryPath(options.resourceDirectories.functions); + } + const functions = await this.pullFunctions({ + code: options.noCode === true ? false : true, + withVariables: options.withVariables, + }); + updatedConfig.functions = functions; + this.setConfigDirectoryPath(originalConfigDirectoryPath); + } - if (shouldPullAll || options.sites) { - const sites = await this.pullSites({ - code: options.noCode === true ? false : true, - withVariables: options.withVariables, - }); - updatedConfig.sites = sites; - } + if (shouldPullAll || options.sites) { + if (options.resourceDirectories?.sites) { + this.setConfigDirectoryPath(options.resourceDirectories.sites); + } + const sites = await this.pullSites({ + code: options.noCode === true ? false : true, + withVariables: options.withVariables, + }); + updatedConfig.sites = sites; + this.setConfigDirectoryPath(originalConfigDirectoryPath); + } - if (shouldPullAll || options.tables) { - const { databases, tables } = await this.pullTables(); - updatedConfig.databases = databases; - updatedConfig.tables = tables; - } + if (shouldPullAll || options.tables) { + const { databases, tables } = await this.pullTables(); + updatedConfig.databases = databases; + updatedConfig.tables = tables; + } - if (options.collections || (shouldPullAll && !skipDeprecated)) { - const { databases, collections } = await this.pullCollections(); - updatedConfig.databases = databases; - updatedConfig.collections = collections; - } + if (options.collections || (shouldPullAll && !skipDeprecated)) { + const { databases, collections } = await this.pullCollections(); + updatedConfig.databases = databases; + updatedConfig.collections = collections; + } - if (shouldPullAll || options.buckets) { - const buckets = await this.pullBuckets(); - updatedConfig.buckets = buckets; - } + if (shouldPullAll || options.buckets) { + const buckets = await this.pullBuckets(); + updatedConfig.buckets = buckets; + } - if (shouldPullAll || options.teams) { - const teams = await this.pullTeams(); - updatedConfig.teams = teams; - } + if (shouldPullAll || options.teams) { + const teams = await this.pullTeams(); + updatedConfig.teams = teams; + } - if (shouldPullAll || options.webhooks) { - const webhooks = await this.pullWebhooks(); - updatedConfig.webhooks = webhooks; - } + if (shouldPullAll || options.webhooks) { + const webhooks = await this.pullWebhooks(); + updatedConfig.webhooks = webhooks; + } - if (shouldPullAll || options.topics) { - const topics = await this.pullMessagingTopics(); - updatedConfig.topics = topics; + if (shouldPullAll || options.topics) { + const topics = await this.pullMessagingTopics(); + updatedConfig.topics = topics; + } + } finally { + this.setConfigDirectoryPath(originalConfigDirectoryPath); } return updatedConfig; @@ -849,7 +871,7 @@ const pullFunctions = async ({ const shouldPullCode = code !== false && allowCodePull === true; const selectedFunctionIds = functionsToCheck.map((f: any) => f.$id); - const pullInstance = await createPullInstance(); + const pullInstance = await createPullInstance({ resource: "functions" }); const functions = await pullInstance.pullFunctions({ code: shouldPullCode, withVariables, @@ -898,7 +920,7 @@ const pullSites = async ({ const shouldPullCode = code !== false && allowCodePull === true; const selectedSiteIds = sitesToCheck.map((s: any) => s.$id); - const pullInstance = await createPullInstance(); + const pullInstance = await createPullInstance({ resource: "sites" }); const sites = await pullInstance.pullSites({ code: shouldPullCode, withVariables, diff --git a/lib/commands/push.ts b/lib/commands/push.ts index 936f71d3..c2aea61e 100644 --- a/lib/commands/push.ts +++ b/lib/commands/push.ts @@ -89,7 +89,7 @@ import { sdkForProject, sdkForConsole } from "../sdks.js"; import { ServiceId, ProtocolId, - AuthMethod, + MethodId, AppwriteException, Client, ImageFormat, @@ -1050,19 +1050,70 @@ export class Push { await projectService.updateSessionAlertPolicy({ enabled: settings.auth.security.sessionAlerts, }); - await projectsService.updateMockNumbers({ - projectId, - numbers: settings.auth.security.mockNumbers, - }); + if (settings.auth.security.mockNumbers !== undefined) { + const remoteMockNumbers: Array<{ number: string; otp: string }> = []; + const limit = 100; + let offset = 0; + let total = 0; + + do { + const response = await projectService.listMockPhones({ + queries: [Query.limit(limit), Query.offset(offset)], + }); + + remoteMockNumbers.push(...response.mockNumbers); + total = response.total; + offset += response.mockNumbers.length; + + if (response.mockNumbers.length === 0) { + break; + } + } while (offset < total); + + const desiredMockNumbersByPhone = new Map( + settings.auth.security.mockNumbers.map((mockNumber) => [ + mockNumber.phone, + mockNumber.otp, + ]), + ); + + for (const remoteMockNumber of remoteMockNumbers) { + const desiredOtp = desiredMockNumbersByPhone.get( + remoteMockNumber.number, + ); + + if (desiredOtp === undefined) { + await projectService.deleteMockPhone({ + number: remoteMockNumber.number, + }); + continue; + } + + if (remoteMockNumber.otp !== desiredOtp) { + await projectService.updateMockPhone({ + number: remoteMockNumber.number, + otp: desiredOtp, + }); + } + + desiredMockNumbersByPhone.delete(remoteMockNumber.number); + } + + for (const [phone, otp] of desiredMockNumbersByPhone) { + await projectService.createMockPhone({ + number: phone, + otp, + }); + } + } } if (settings.auth.methods) { this.log("Applying auth methods statuses ..."); for (const [method, status] of Object.entries(settings.auth.methods)) { - await projectsService.updateAuthStatus({ - projectId, - method: method as AuthMethod, - status: status, + await projectService.updateAuthMethod({ + methodId: method as MethodId, + enabled: status, }); } } @@ -2781,6 +2832,18 @@ async function createPushInstance( return new Push(projectClient, consoleClient, silent); } +function withResolvedResourcePaths( + resource: "functions" | "sites", + resources: T[], +): T[] { + return resources.map((item) => ({ + ...item, + path: item.path + ? localConfig.resolveResourcePath(resource, item.path) + : item.path, + })); +} + const pushResources = async ({ skipDeprecated = false, functionOptions, @@ -2835,8 +2898,8 @@ const pushResources = async ({ projectId: project.projectId ?? "", projectName: project.projectName, settings: project.projectSettings, - functions, - sites, + functions: withResolvedResourcePaths("functions", functions), + sites: withResolvedResourcePaths("sites", sites), collections: localConfig.getCollections(), databases: localConfig.getDatabases(), tables: localConfig.getTables(), @@ -3054,13 +3117,16 @@ const pushSite = async ({ const pushStartTime = Date.now(); const pushInstance = await createPushInstance(); - const result = await pushInstance.pushSites(sites, { - async: asyncDeploy, - code: shouldPushCode, - activate: shouldActivate ?? true, - withVariables, - logs, - }); + const result = await pushInstance.pushSites( + withResolvedResourcePaths("sites", sites), + { + async: asyncDeploy, + code: shouldPushCode, + activate: shouldActivate ?? true, + withVariables, + logs, + }, + ); const { successfullyPushed, @@ -3212,13 +3278,16 @@ const pushFunction = async ({ const pushStartTime = Date.now(); const pushInstance = await createPushInstance(); - const result = await pushInstance.pushFunctions(functions, { - async: asyncDeploy, - code: shouldPushCode, - activate: shouldActivate ?? true, - withVariables, - logs, - }); + const result = await pushInstance.pushFunctions( + withResolvedResourcePaths("functions", functions), + { + async: asyncDeploy, + code: shouldPushCode, + activate: shouldActivate ?? true, + withVariables, + logs, + }, + ); const { successfullyPushed, diff --git a/lib/commands/run.ts b/lib/commands/run.ts index 73f076e4..933457e0 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -151,16 +151,9 @@ const runFunction = async ({ process.exit(); }); - const logsPath = path.join( - localConfig.getDirname(), - func.path, - ".appwrite/logs.txt", - ); - const errorsPath = path.join( - localConfig.getDirname(), - func.path, - ".appwrite/errors.txt", - ); + const functionPath = localConfig.resolveResourcePath("functions", func.path); + const logsPath = path.join(functionPath, ".appwrite/logs.txt"); + const errorsPath = path.join(functionPath, ".appwrite/errors.txt"); if (!fs.existsSync(path.dirname(logsPath))) { fs.mkdirSync(path.dirname(logsPath), { recursive: true }); @@ -197,7 +190,6 @@ const runFunction = async ({ } } - const functionPath = path.join(localConfig.getDirname(), func.path); const envPath = path.join(functionPath, ".env"); if (fs.existsSync(envPath)) { const env = parseDotenv(fs.readFileSync(envPath).toString() ?? ""); @@ -285,7 +277,7 @@ const runFunction = async ({ chokidar .watch(".", { - cwd: path.join(localConfig.getDirname(), func.path), + cwd: functionPath, ignoreInitial: true, ignored: (xpath: string) => { const relativePath = path.relative(functionPath, xpath); diff --git a/lib/commands/schema.ts b/lib/commands/schema.ts index 035e5c2e..daf1b856 100644 --- a/lib/commands/schema.ts +++ b/lib/commands/schema.ts @@ -4,16 +4,21 @@ import { ConfigSchema } from "./config.js"; import { Pull, PullOptions } from "./pull.js"; import { Push, PushOptions } from "./push.js"; import { parseWithBetterErrors } from "./utils/error-formatter.js"; -import { JSONBig } from "../json.js"; -import * as fs from "fs"; import * as path from "path"; import { TypeScriptDatabasesGenerator } from "./generators/typescript/databases.js"; +import { + getLocalConfigResourceDirnames, + readLocalConfigFile, + resolveLocalConfigResourcePaths, + writeLocalConfigFile, +} from "../config.js"; export class Schema { private pullCommand: Pull; private pushCommand: Push; private pullCommandSilent: Pull; + private configPaths = new WeakMap(); public db: TypeScriptDatabasesGenerator; @@ -61,9 +66,15 @@ export class Schema { options: PullOptions, configPath?: string, ): Promise { - const updatedConfig = await this.pullCommand.pullResources(config, options); - if (configPath) { - this.write(updatedConfig, configPath); + const resolvedConfigPath = configPath + ? path.resolve(configPath) + : this.configPaths.get(config); + const updatedConfig = await this.pullCommand.pullResources( + config, + this.withResourceDirectories(options, resolvedConfigPath), + ); + if (resolvedConfigPath) { + this.write(updatedConfig, resolvedConfigPath); } return updatedConfig; } @@ -83,14 +94,21 @@ export class Schema { options: PushOptions, configPath?: string, ): Promise { - await this.pushCommand.pushResources(config, options); + const resolvedConfigPath = configPath + ? path.resolve(configPath) + : this.configPaths.get(config); + const pushConfig = resolvedConfigPath + ? resolveLocalConfigResourcePaths(config, resolvedConfigPath) + : config; + + await this.pushCommand.pushResources(pushConfig, options); const updatedConfig = await this.pullCommandSilent.pullResources( config, - options, + this.withResourceDirectories(options, resolvedConfigPath), ); - if (configPath) { - this.write(updatedConfig, configPath); + if (resolvedConfigPath) { + this.write(updatedConfig, resolvedConfigPath); } return updatedConfig; } @@ -101,8 +119,11 @@ export class Schema { * @param path - The path to the file to read. * @returns The configuration object. */ - public read(path: string): ConfigType { - return JSONBig.parse(fs.readFileSync(path, "utf8")) as ConfigType; + public read(filePath: string): ConfigType { + const resolvedPath = path.resolve(filePath); + const config = readLocalConfigFile(resolvedPath); + this.configPaths.set(config, resolvedPath); + return config; } /** @@ -114,7 +135,21 @@ export class Schema { */ public write(config: ConfigType, filePath: string): void { const resolvedPath = path.resolve(filePath); - const content = JSONBig.stringify(config, null, 4); - fs.writeFileSync(resolvedPath, content); + writeLocalConfigFile(config, resolvedPath); + this.configPaths.set(config, resolvedPath); + } + + private withResourceDirectories( + options: PullOptions, + configPath?: string, + ): PullOptions { + if (!configPath) { + return options; + } + + return { + ...options, + resourceDirectories: getLocalConfigResourceDirnames(configPath), + }; } } diff --git a/lib/commands/services/account.ts b/lib/commands/services/account.ts index 89c5bbb4..da7e5f33 100644 --- a/lib/commands/services/account.ts +++ b/lib/commands/services/account.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Account } from "@appwrite.io/console"; @@ -389,7 +390,7 @@ const accountUpdatePrefsCommand = account .action( actionRunner( async ({ prefs }) => - parse(await (await getAccountClient()).updatePrefs(JSON.parse(prefs))), + parse(await (await getAccountClient()).updatePrefs(parseJsonObject(prefs, "--prefs"))), ), ); @@ -489,7 +490,7 @@ If there is already an active session, the new session will be attached to the l A user is limited to 10 active sessions at a time by default. Learn more about session limits (https://appwrite.io/docs/authentication-security#limits). `) - .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom.`) + .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom.`) .option(`--success `, `URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an open redirect (https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--failure `, `URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an open redirect (https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--scopes [scopes...]`, `A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of 100 scopes are allowed, each 4096 characters long.`) @@ -666,7 +667,7 @@ const accountCreateOAuth2TokenCommand = account If authentication succeeds, \`userId\` and \`secret\` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the Create session (https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint. A user is limited to 10 active sessions at a time by default. Learn more about session limits (https://appwrite.io/docs/authentication-security#limits).`) - .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom.`) + .requiredOption(`--provider `, `OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom.`) .option(`--success `, `URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an open redirect (https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--failure `, `URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an open redirect (https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--scopes [scopes...]`, `A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of 100 scopes are allowed, each 4096 characters long.`) diff --git a/lib/commands/services/databases.ts b/lib/commands/services/databases.ts index d6acdb4d..a62f7afb 100644 --- a/lib/commands/services/databases.ts +++ b/lib/commands/services/databases.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Databases } from "@appwrite.io/console"; @@ -1156,7 +1157,7 @@ const databasesCreateDocumentCommand = databases .action( actionRunner( async ({ databaseId, collectionId, documentId, data, permissions, transactionId }) => - parse(await (await getDatabasesClient()).createDocument(databaseId, collectionId, documentId, JSON.parse(data), permissions, transactionId)), + parse(await (await getDatabasesClient()).createDocument(databaseId, collectionId, documentId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); @@ -1210,7 +1211,7 @@ const databasesUpdateDocumentsCommand = databases .action( actionRunner( async ({ databaseId, collectionId, data, queries, transactionId, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => - parse(await (await getDatabasesClient()).updateDocuments(databaseId, collectionId, JSON.parse(data), buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), transactionId)), + parse(await (await getDatabasesClient()).updateDocuments(databaseId, collectionId, parseJsonObject(data, "--data"), buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), transactionId)), ), ); @@ -1266,7 +1267,7 @@ const databasesUpsertDocumentCommand = databases .action( actionRunner( async ({ databaseId, collectionId, documentId, data, permissions, transactionId }) => - parse(await (await getDatabasesClient()).upsertDocument(databaseId, collectionId, documentId, JSON.parse(data), permissions, transactionId)), + parse(await (await getDatabasesClient()).upsertDocument(databaseId, collectionId, documentId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); @@ -1283,7 +1284,7 @@ const databasesUpdateDocumentCommand = databases .action( actionRunner( async ({ databaseId, collectionId, documentId, data, permissions, transactionId }) => - parse(await (await getDatabasesClient()).updateDocument(databaseId, collectionId, documentId, JSON.parse(data), permissions, transactionId)), + parse(await (await getDatabasesClient()).updateDocument(databaseId, collectionId, documentId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); diff --git a/lib/commands/services/functions.ts b/lib/commands/services/functions.ts index 73e68e2f..db803b47 100644 --- a/lib/commands/services/functions.ts +++ b/lib/commands/services/functions.ts @@ -14,6 +14,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Functions } from "@appwrite.io/console"; @@ -464,7 +465,7 @@ const functionsCreateExecutionCommand = functions .action( actionRunner( async ({ functionId, body, async, path, method, headers, scheduledAt }) => - parse(await (await getFunctionsClient()).createExecution(functionId, body, async, path, method, JSON.parse(headers), scheduledAt)), + parse(await (await getFunctionsClient()).createExecution(functionId, body, async, path, method, parseJsonObject(headers, "--headers"), scheduledAt)), ), ); diff --git a/lib/commands/services/graphql.ts b/lib/commands/services/graphql.ts index 35efb201..c1d76342 100644 --- a/lib/commands/services/graphql.ts +++ b/lib/commands/services/graphql.ts @@ -7,6 +7,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Graphql } from "@appwrite.io/console"; @@ -33,7 +34,7 @@ const graphqlQueryCommand = graphql .action( actionRunner( async ({ query }) => - parse(await (await getGraphqlClient()).query(JSON.parse(query))), + parse(await (await getGraphqlClient()).query(parseJsonObject(query, "--query"))), ), ); @@ -45,7 +46,7 @@ const graphqlMutationCommand = graphql .action( actionRunner( async ({ query }) => - parse(await (await getGraphqlClient()).mutation(JSON.parse(query))), + parse(await (await getGraphqlClient()).mutation(parseJsonObject(query, "--query"))), ), ); diff --git a/lib/commands/services/messaging.ts b/lib/commands/services/messaging.ts index f69721a8..e3c22457 100644 --- a/lib/commands/services/messaging.ts +++ b/lib/commands/services/messaging.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Messaging } from "@appwrite.io/console"; @@ -164,7 +165,7 @@ const messagingCreatePushCommand = messaging .action( actionRunner( async ({ messageId, title, body, topics, users, targets, data, action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority }) => - parse(await (await getMessagingClient()).createPush(messageId, title, body, topics, users, targets, JSON.parse(data), action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority)), + parse(await (await getMessagingClient()).createPush(messageId, title, body, topics, users, targets, parseJsonObject(data, "--data"), action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority)), ), ); @@ -210,7 +211,7 @@ const messagingUpdatePushCommand = messaging .action( actionRunner( async ({ messageId, topics, users, targets, title, body, data, action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority }) => - parse(await (await getMessagingClient()).updatePush(messageId, topics, users, targets, title, body, JSON.parse(data), action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority)), + parse(await (await getMessagingClient()).updatePush(messageId, topics, users, targets, title, body, parseJsonObject(data, "--data"), action, image, icon, sound, color, tag, badge, draft, scheduledAt, contentAvailable, critical, priority)), ), ); @@ -433,7 +434,7 @@ const messagingCreateFcmProviderCommand = messaging .action( actionRunner( async ({ providerId, name, serviceAccountJson, enabled }) => - parse(await (await getMessagingClient()).createFcmProvider(providerId, name, JSON.parse(serviceAccountJson), enabled)), + parse(await (await getMessagingClient()).createFcmProvider(providerId, name, parseJsonObject(serviceAccountJson, "--service-account-json"), enabled)), ), ); @@ -453,7 +454,7 @@ const messagingUpdateFcmProviderCommand = messaging .action( actionRunner( async ({ providerId, name, enabled, serviceAccountJson }) => - parse(await (await getMessagingClient()).updateFcmProvider(providerId, name, enabled, JSON.parse(serviceAccountJson))), + parse(await (await getMessagingClient()).updateFcmProvider(providerId, name, enabled, parseJsonObject(serviceAccountJson, "--service-account-json"))), ), ); diff --git a/lib/commands/services/project.ts b/lib/commands/services/project.ts index 128e7ba6..b30e5580 100644 --- a/lib/commands/services/project.ts +++ b/lib/commands/services/project.ts @@ -31,129 +31,1074 @@ export const project = new Command("project") helpWidth: process.stdout.columns || 80, }); +const projectDeleteCommand = project + .command(`delete`) + .description(`Delete a project.`) + .action( + actionRunner( + async () => parse(await (await getProjectClient()).delete()), + ), + ); + + +const projectUpdateAuthMethodCommand = project + .command(`update-auth-method`) + .description(`Update properties of a specific auth method. Use this endpoint to enable or disable a method in your project. `) + .requiredOption(`--method-id `, `Auth Method ID. Possible values: email-password,magic-url,email-otp,anonymous,invites,jwt,phone`) + .requiredOption(`--enabled `, `Auth method status.`, parseBool) + .action( + actionRunner( + async ({ methodId, enabled }) => + parse(await (await getProjectClient()).updateAuthMethod(methodId, enabled)), + ), + ); + + const projectUpdateCanonicalEmailsCommand = project .command(`update-canonical-emails`) .description(`Configure if canonical emails (alias subaddresses and emails with suffixes) are allowed during new users sign-ups in this project.`) .requiredOption(`--enabled `, `Set whether or not to require canonical email addresses during signup and email updates.`, parseBool) .action( actionRunner( - async ({ enabled }) => - parse(await (await getProjectClient()).updateCanonicalEmails(enabled)), + async ({ enabled }) => + parse(await (await getProjectClient()).updateCanonicalEmails(enabled)), + ), + ); + + +const projectUpdateDisposableEmailsCommand = project + .command(`update-disposable-emails`) + .description(`Configure if disposable emails (emails of known temporary domains) are allowed during new users sign-ups in this project.`) + .requiredOption(`--enabled `, `Set whether or not to block disposable email addresses during signup and email updates.`, parseBool) + .action( + actionRunner( + async ({ enabled }) => + parse(await (await getProjectClient()).updateDisposableEmails(enabled)), + ), + ); + + +const projectUpdateFreeEmailsCommand = project + .command(`update-free-emails`) + .description(`Configure if free emails (non-commercial and not a custom domain) are allowed during new users sign-ups in this project.`) + .requiredOption(`--enabled `, `Set whether or not to block free email addresses during signup and email updates.`, parseBool) + .action( + actionRunner( + async ({ enabled }) => + parse(await (await getProjectClient()).updateFreeEmails(enabled)), + ), + ); + + +const projectListKeysCommand = project + .command(`list-keys`) + .description(`Get a list of all API keys from the current project.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire, accessedAt, name, scopes`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--where `, `Filter using a simple comparison expression. Repeat for multiple filters. Supports field=value, field!=value, field>value, field>=value, field collectQueryValue(parseWhereQuery(value), previous)) + .option(`--sort-asc `, `Sort results by an attribute in ascending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--sort-desc `, `Sort results by an attribute in descending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .option(`--cursor-after `, `Return results after this cursor ID.`) + .option(`--cursor-before `, `Return results before this cursor ID.`) + .action( + actionRunner( + async ({ queries, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => + parse(await (await getProjectClient()).listKeys(buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total)), + ), + ); + + +const projectCreateKeyCommand = project + .command(`create-key`) + .description(`Create a new API key. It's recommended to have multiple API keys with strict scopes for separate functions within your project. + +You can also create an ephemeral API key if you need a short-lived key instead.`) + .requiredOption(`--key-id `, `Key ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) + .requiredOption(`--name `, `Key name. Max length: 128 chars.`) + .requiredOption(`--scopes [scopes...]`, `Key scopes list. Maximum of 100 scopes are allowed.`) + .option(`--expire `, `Expiration time in ISO 8601 (https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.`) + .action( + actionRunner( + async ({ keyId, name, scopes, expire }) => + parse(await (await getProjectClient()).createKey(keyId, name, scopes, expire)), + ), + ); + + +const projectCreateEphemeralKeyCommand = project + .command(`create-ephemeral-key`) + .description(`Create a new ephemeral API key. It's recommended to have multiple API keys with strict scopes for separate functions within your project. + +You can also create a standard API key if you need a longer-lived key instead.`) + .requiredOption(`--scopes [scopes...]`, `Key scopes list. Maximum of 100 scopes are allowed.`) + .requiredOption(`--duration `, `Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds.`, parseInteger) + .action( + actionRunner( + async ({ scopes, duration }) => + parse(await (await getProjectClient()).createEphemeralKey(scopes, duration)), + ), + ); + + +const projectGetKeyCommand = project + .command(`get-key`) + .description(`Get a key by its unique ID. `) + .requiredOption(`--key-id `, `Key ID.`) + .action( + actionRunner( + async ({ keyId }) => + parse(await (await getProjectClient()).getKey(keyId)), + ), + ); + + +const projectUpdateKeyCommand = project + .command(`update-key`) + .description(`Update a key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key.`) + .requiredOption(`--key-id `, `Key ID.`) + .requiredOption(`--name `, `Key name. Max length: 128 chars.`) + .requiredOption(`--scopes [scopes...]`, `Key scopes list. Maximum of 100 scopes are allowed.`) + .option(`--expire `, `Expiration time in ISO 8601 (https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.`) + .action( + actionRunner( + async ({ keyId, name, scopes, expire }) => + parse(await (await getProjectClient()).updateKey(keyId, name, scopes, expire)), + ), + ); + + +const projectDeleteKeyCommand = project + .command(`delete-key`) + .description(`Delete a key by its unique ID. Once deleted, the key can no longer be used to authenticate API calls.`) + .requiredOption(`--key-id `, `Key ID.`) + .action( + actionRunner( + async ({ keyId }) => + parse(await (await getProjectClient()).deleteKey(keyId)), + ), + ); + + +const projectUpdateLabelsCommand = project + .command(`update-labels`) + .description(`Update the project labels. Labels can be used to easily filter projects in an organization.`) + .requiredOption(`--labels [labels...]`, `Array of project labels. Replaces the previous labels. Maximum of 1000 labels are allowed, each up to 36 alphanumeric characters long.`) + .action( + actionRunner( + async ({ labels }) => + parse(await (await getProjectClient()).updateLabels(labels)), + ), + ); + + +const projectListMockPhonesCommand = project + .command(`list-mock-phones`) + .description(`Get a list of all mock phones in the project. This endpoint returns an array of all mock phones and their OTPs.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common pagination prefer --limit and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Only supported methods are limit and offset`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .action( + actionRunner( + async ({ queries, total, limit, offset }) => + parse(await (await getProjectClient()).listMockPhones(buildQueries({ queries, limit, offset }), total)), + ), + ); + + +const projectCreateMockPhoneCommand = project + .command(`create-mock-phone`) + .description(`Create a new mock phone for your project. Use this endpoint to register a mock phone number and its sign-in OTP for your testers.`) + .requiredOption(`--number `, `Phone number to associate with the mock phone. Must be a valid E.164 formatted phone number.`) + .requiredOption(`--otp `, `One-time password (OTP) to associate with the mock phone. Must be a 6-digit numeric code.`) + .action( + actionRunner( + async ({ number, otp }) => + parse(await (await getProjectClient()).createMockPhone(number, otp)), + ), + ); + + +const projectGetMockPhoneCommand = project + .command(`get-mock-phone`) + .description(`Get a mock phone by its unique number. This endpoint returns the mock phone's OTP.`) + .requiredOption(`--number `, `Phone number associated with the mock phone. Must be a valid E.164 formatted phone number.`) + .action( + actionRunner( + async ({ number }) => + parse(await (await getProjectClient()).getMockPhone(number)), + ), + ); + + +const projectUpdateMockPhoneCommand = project + .command(`update-mock-phone`) + .description(`Update a mock phone by its unique number. Use this endpoint to update the mock phone's OTP.`) + .requiredOption(`--number `, `Phone number associated with the mock phone. Must be a valid E.164 formatted phone number.`) + .requiredOption(`--otp `, `One-time password (OTP) to associate with the mock phone. Must be a 6-digit numeric code.`) + .action( + actionRunner( + async ({ number, otp }) => + parse(await (await getProjectClient()).updateMockPhone(number, otp)), + ), + ); + + +const projectDeleteMockPhoneCommand = project + .command(`delete-mock-phone`) + .description(`Delete a mock phone by its unique number. This endpoint removes the mock phone and its OTP configuration from the project.`) + .requiredOption(`--number `, `Phone number associated with the mock phone. Must be a valid E.164 formatted phone number.`) + .action( + actionRunner( + async ({ number }) => + parse(await (await getProjectClient()).deleteMockPhone(number)), + ), + ); + + +const projectListOAuth2ProvidersCommand = project + .command(`list-o-auth-2-providers`) + .description(`Get a list of all OAuth2 providers supported by the server, along with the project's configuration for each. Credential fields are write-only and always returned empty.`) + .action( + actionRunner( + async () => parse(await (await getProjectClient()).listOAuth2Providers()), + ), + ); + + +const projectUpdateOAuth2AmazonCommand = project + .command(`update-o-auth-2-amazon`) + .description(`Update the project OAuth2 Amazon configuration.`) + .option(`--client-id `, `'Client ID' of Amazon OAuth2 app. For example: amzn1.application-oa2-client.87400c00000000000000000000063d5b2`) + .option(`--client-secret `, `'Client Secret' of Amazon OAuth2 app. For example: 79ffe4000000000000000000000000000000000000000000000000000002de55`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Amazon(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2AppleCommand = project + .command(`update-o-auth-2-apple`) + .description(`Update the project OAuth2 Apple configuration.`) + .option(`--service-id `, `'Service ID' of Apple OAuth2 app. For example: ip.appwrite.app.web`) + .option(`--key-id `, `'Key ID' of Apple OAuth2 app. For example: P4000000N8`) + .option(`--team-id `, `'Team ID' of Apple OAuth2 app. For example: D4000000R6`) + .option(`--p-8-file `, `Contents of the Apple OAuth2 app .p8 private key file. The secret key wrapped by the PEM markers is 200 characters long. For example: -----BEGIN PRIVATE KEY-----MIGTAg...jy2Xbna-----END PRIVATE KEY-----`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ serviceId, keyId, teamId, p8File, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Apple(serviceId, keyId, teamId, p8File, enabled)), + ), + ); + + +const projectUpdateOAuth2Auth0Command = project + .command(`update-o-auth-2-auth-0`) + .description(`Update the project OAuth2 Auth0 configuration.`) + .option(`--client-id `, `'Client ID' of Auth0 OAuth2 app. For example: OaOkIA000000000000000000005KLSYq`) + .option(`--client-secret `, `'Client Secret' of Auth0 OAuth2 app. For example: zXz0000-00000000000000000000000000000-00000000000000000000PJafnF`) + .option(`--endpoint `, `Domain of Auth0 instance. For example: example.us.auth0.com`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, endpoint, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Auth0(clientId, clientSecret, endpoint, enabled)), + ), + ); + + +const projectUpdateOAuth2AuthentikCommand = project + .command(`update-o-auth-2-authentik`) + .description(`Update the project OAuth2 Authentik configuration.`) + .option(`--client-id `, `'Client ID' of Authentik OAuth2 app. For example: dTKOPa0000000000000000000000000000e7G8hv`) + .option(`--client-secret `, `'Client Secret' of Authentik OAuth2 app. For example: ntQadq000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Hp5WK`) + .option(`--endpoint `, `Domain of Authentik instance. For example: example.authentik.com`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, endpoint, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Authentik(clientId, clientSecret, endpoint, enabled)), + ), + ); + + +const projectUpdateOAuth2AutodeskCommand = project + .command(`update-o-auth-2-autodesk`) + .description(`Update the project OAuth2 Autodesk configuration.`) + .option(`--client-id `, `'Client ID' of Autodesk OAuth2 app. For example: 5zw90v00000000000000000000kVYXN7`) + .option(`--client-secret `, `'Client Secret' of Autodesk OAuth2 app. For example: 7I000000000000MW`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Autodesk(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2BitbucketCommand = project + .command(`update-o-auth-2-bitbucket`) + .description(`Update the project OAuth2 Bitbucket configuration.`) + .option(`--key `, `'Key' of Bitbucket OAuth2 app. For example: Knt70000000000ByRc`) + .option(`--secret `, `'Secret' of Bitbucket OAuth2 app. For example: NMfLZJ00000000000000000000TLQdDx`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ key, secret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Bitbucket(key, secret, enabled)), + ), + ); + + +const projectUpdateOAuth2BitlyCommand = project + .command(`update-o-auth-2-bitly`) + .description(`Update the project OAuth2 Bitly configuration.`) + .option(`--client-id `, `'Client ID' of Bitly OAuth2 app. For example: d95151000000000000000000000000000067af9b`) + .option(`--client-secret `, `'Client Secret' of Bitly OAuth2 app. For example: a13e250000000000000000000000000000d73095`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Bitly(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2BoxCommand = project + .command(`update-o-auth-2-box`) + .description(`Update the project OAuth2 Box configuration.`) + .option(`--client-id `, `'Client ID' of Box OAuth2 app. For example: deglcs00000000000000000000x2og6y`) + .option(`--client-secret `, `'Client Secret' of Box OAuth2 app. For example: OKM1f100000000000000000000eshEif`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Box(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2DailymotionCommand = project + .command(`update-o-auth-2-dailymotion`) + .description(`Update the project OAuth2 Dailymotion configuration.`) + .option(`--api-key `, `'API Key' of Dailymotion OAuth2 app. For example: 07a9000000000000067f`) + .option(`--api-secret `, `'API Secret' of Dailymotion OAuth2 app. For example: a399a90000000000000000000000000000d90639`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ apiKey, apiSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Dailymotion(apiKey, apiSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2DiscordCommand = project + .command(`update-o-auth-2-discord`) + .description(`Update the project OAuth2 Discord configuration.`) + .option(`--client-id `, `'Client ID' of Discord OAuth2 app. For example: 950722000000343754`) + .option(`--client-secret `, `'Client Secret' of Discord OAuth2 app. For example: YmPXnM000000000000000000002zFg5D`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Discord(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2DisqusCommand = project + .command(`update-o-auth-2-disqus`) + .description(`Update the project OAuth2 Disqus configuration.`) + .option(`--public-key `, `'Public Key, also known as API Key' of Disqus OAuth2 app. For example: cgegH70000000000000000000000000000000000000000000000000000Hr1nYX`) + .option(`--secret-key `, `'Secret Key, also known as API Secret' of Disqus OAuth2 app. For example: W7Bykj00000000000000000000000000000000000000000000000000003o43w9`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ publicKey, secretKey, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Disqus(publicKey, secretKey, enabled)), + ), + ); + + +const projectUpdateOAuth2DropboxCommand = project + .command(`update-o-auth-2-dropbox`) + .description(`Update the project OAuth2 Dropbox configuration.`) + .option(`--app-key `, `'App Key' of Dropbox OAuth2 app. For example: jl000000000009t`) + .option(`--app-secret `, `'App Secret' of Dropbox OAuth2 app. For example: g200000000000vw`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ appKey, appSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Dropbox(appKey, appSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2EtsyCommand = project + .command(`update-o-auth-2-etsy`) + .description(`Update the project OAuth2 Etsy configuration.`) + .option(`--key-string `, `'Keystring' of Etsy OAuth2 app. For example: nsgzxh0000000000008j85a2`) + .option(`--shared-secret `, `'Shared Secret' of Etsy OAuth2 app. For example: tp000000ru`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ keyString, sharedSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Etsy(keyString, sharedSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2FacebookCommand = project + .command(`update-o-auth-2-facebook`) + .description(`Update the project OAuth2 Facebook configuration.`) + .option(`--app-id `, `'App ID' of Facebook OAuth2 app. For example: 260600000007694`) + .option(`--app-secret `, `'App Secret' of Facebook OAuth2 app. For example: 2d0b2800000000000000000000d38af4`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ appId, appSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Facebook(appId, appSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2FigmaCommand = project + .command(`update-o-auth-2-figma`) + .description(`Update the project OAuth2 Figma configuration.`) + .option(`--client-id `, `'Client ID' of Figma OAuth2 app. For example: byay5H0000000000VtiI40`) + .option(`--client-secret `, `'Client Secret' of Figma OAuth2 app. For example: yEpOYn0000000000000000004iIsU5`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Figma(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2FusionAuthCommand = project + .command(`update-o-auth-2-fusion-auth`) + .description(`Update the project OAuth2 FusionAuth configuration.`) + .option(`--client-id `, `'Client ID' of FusionAuth OAuth2 app. For example: b2222c00-0000-0000-0000-000000862097`) + .option(`--client-secret `, `'Client Secret' of FusionAuth OAuth2 app. For example: Jx4s0C0000000000000000000000000000000wGqLsc`) + .option(`--endpoint `, `Domain of FusionAuth instance. For example: example.fusionauth.io`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, endpoint, enabled }) => + parse(await (await getProjectClient()).updateOAuth2FusionAuth(clientId, clientSecret, endpoint, enabled)), + ), + ); + + +const projectUpdateOAuth2GitHubCommand = project + .command(`update-o-auth-2-git-hub`) + .description(`Update the project OAuth2 GitHub configuration.`) + .option(`--client-id `, `'OAuth2 app Client ID, or App ID' of GitHub OAuth2 app. For example: e4d87900000000540733. Example of wrong value: 370006`) + .option(`--client-secret `, `'Client Secret' of GitHub OAuth2 app. For example: 5e07c00000000000000000000000000000198bcc`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2GitHub(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2GitlabCommand = project + .command(`update-o-auth-2-gitlab`) + .description(`Update the project OAuth2 Gitlab configuration.`) + .option(`--application-id `, `'Application ID' of Gitlab OAuth2 app. For example: d41ffe0000000000000000000000000000000000000000000000000000d5e252`) + .option(`--secret `, `'Secret' of Gitlab OAuth2 app. For example: gloas-838cfa0000000000000000000000000000000000000000000000000000ecbb38`) + .option(`--endpoint `, `Endpoint URL of self-hosted GitLab instance. For example: https://gitlab.com`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ applicationId, secret, endpoint, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Gitlab(applicationId, secret, endpoint, enabled)), + ), + ); + + +const projectUpdateOAuth2GoogleCommand = project + .command(`update-o-auth-2-google`) + .description(`Update the project OAuth2 Google configuration.`) + .option(`--client-id `, `'Client ID' of Google OAuth2 app. For example: 120000000095-92ifjb00000000000000000000g7ijfb.apps.googleusercontent.com`) + .option(`--client-secret `, `'Client Secret' of Google OAuth2 app. For example: GOCSPX-2k8gsR0000000000000000VNahJj`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Google(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2KeycloakCommand = project + .command(`update-o-auth-2-keycloak`) + .description(`Update the project OAuth2 Keycloak configuration.`) + .option(`--client-id `, `'Client ID' of Keycloak OAuth2 app. For example: appwrite-o0000000st-app`) + .option(`--client-secret `, `'Client Secret' of Keycloak OAuth2 app. For example: jdjrJd00000000000000000000HUsaZO`) + .option(`--endpoint `, `Domain of Keycloak instance. For example: keycloak.example.com`) + .option(`--realm-name `, `Keycloak realm name. For example: appwrite-realm`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, endpoint, realmName, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Keycloak(clientId, clientSecret, endpoint, realmName, enabled)), + ), + ); + + +const projectUpdateOAuth2KickCommand = project + .command(`update-o-auth-2-kick`) + .description(`Update the project OAuth2 Kick configuration.`) + .option(`--client-id `, `'Client ID' of Kick OAuth2 app. For example: 01KQ7C00000000000001MFHS32`) + .option(`--client-secret `, `'Client Secret' of Kick OAuth2 app. For example: 34ac5600000000000000000000000000000000000000000000000000e830c8b`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Kick(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2LinkedinCommand = project + .command(`update-o-auth-2-linkedin`) + .description(`Update the project OAuth2 Linkedin configuration.`) + .option(`--client-id `, `'Client ID' of Linkedin OAuth2 app. For example: 770000000000dv`) + .option(`--primary-client-secret `, `'Primary Client Secret or Secondary Client Secret' of Linkedin OAuth2 app. For example: WPL_AP1.2Bf0000000000000./HtlYw==`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, primaryClientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Linkedin(clientId, primaryClientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2MicrosoftCommand = project + .command(`update-o-auth-2-microsoft`) + .description(`Update the project OAuth2 Microsoft configuration.`) + .option(`--application-id `, `'Entra ID Application ID, also known as Client ID' of Microsoft OAuth2 app. For example: 00001111-aaaa-2222-bbbb-3333cccc4444`) + .option(`--application-secret `, `'Entra ID Application Secret, also known as Client Secret' of Microsoft OAuth2 app. For example: A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u`) + .option(`--tenant `, `Microsoft Entra ID tenant identifier. Use 'common', 'organizations', 'consumers' or a specific tenant ID. For example: common`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ applicationId, applicationSecret, tenant, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Microsoft(applicationId, applicationSecret, tenant, enabled)), + ), + ); + + +const projectUpdateOAuth2NotionCommand = project + .command(`update-o-auth-2-notion`) + .description(`Update the project OAuth2 Notion configuration.`) + .option(`--oauth-client-id `, `'OAuth Client ID' of Notion OAuth2 app. For example: 341d8700-0000-0000-0000-000000446ee3`) + .option(`--oauth-client-secret `, `'OAuth Client Secret' of Notion OAuth2 app. For example: secret_dLUr4b000000000000000000000000000000lFHAa9`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ oauthClientId, oauthClientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Notion(oauthClientId, oauthClientSecret, enabled)), ), ); -const projectUpdateDisposableEmailsCommand = project - .command(`update-disposable-emails`) - .description(`Configure if disposable emails (emails of known temporary domains) are allowed during new users sign-ups in this project.`) - .requiredOption(`--enabled `, `Set whether or not to block disposable email addresses during signup and email updates.`, parseBool) +const projectUpdateOAuth2OidcCommand = project + .command(`update-o-auth-2-oidc`) + .description(`Update the project OAuth2 Oidc configuration.`) + .option(`--client-id `, `'Client ID' of Oidc OAuth2 app. For example: qibI2x0000000000000000000000000006L2YFoG`) + .option(`--client-secret `, `'Client Secret' of Oidc OAuth2 app. For example: Ah68ed000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003qpcHV`) + .option(`--well-known-url `, `OpenID Connect well-known configuration URL. When provided, authorization, token, and user info endpoints can be discovered automatically. For example: https://myoauth.com/.well-known/openid-configuration`) + .option(`--authorization-url `, `OpenID Connect authorization endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/authorize`) + .option(`--token-url `, `OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token`) + .option(`--user-info-url `, `OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ enabled }) => - parse(await (await getProjectClient()).updateDisposableEmails(enabled)), + async ({ clientId, clientSecret, wellKnownUrl, authorizationUrl, tokenUrl, userInfoUrl, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Oidc(clientId, clientSecret, wellKnownUrl, authorizationUrl, tokenUrl, userInfoUrl, enabled)), ), ); -const projectUpdateFreeEmailsCommand = project - .command(`update-free-emails`) - .description(`Configure if free emails (non-commercial and not a custom domain) are allowed during new users sign-ups in this project.`) - .requiredOption(`--enabled `, `Set whether or not to block free email addresses during signup and email updates.`, parseBool) +const projectUpdateOAuth2OktaCommand = project + .command(`update-o-auth-2-okta`) + .description(`Update the project OAuth2 Okta configuration.`) + .option(`--client-id `, `'Client ID' of Okta OAuth2 app. For example: 0oa00000000000000698`) + .option(`--client-secret `, `'Client Secret' of Okta OAuth2 app. For example: Kiq0000000000000000000000000000000000000-00000000000H2L5-3SJ-vRV`) + .option(`--domain `, `Okta company domain. Required when enabling the provider. For example: trial-6400025.okta.com. Example of wrong value: trial-6400025-admin.okta.com, or https://trial-6400025.okta.com/`) + .option(`--authorization-server-id `, `Custom Authorization Servers. Optional, can be left empty or unconfigured. For example: aus000000000000000h7z`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ enabled }) => - parse(await (await getProjectClient()).updateFreeEmails(enabled)), + async ({ clientId, clientSecret, domain, authorizationServerId, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Okta(clientId, clientSecret, domain, authorizationServerId, enabled)), ), ); -const projectListKeysCommand = project - .command(`list-keys`) - .description(`Get a list of all API keys from the current project.`) - .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common filtering, sorting, and pagination prefer --where, --sort-asc, --sort-desc, --limit, and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire, accessedAt, name, scopes`) +const projectUpdateOAuth2PaypalCommand = project + .command(`update-o-auth-2-paypal`) + .description(`Update the project OAuth2 Paypal configuration.`) + .option(`--client-id `, `'Client ID' of Paypal OAuth2 app. For example: AdhIEG7-000000000000-0000000000000000000000000000000-0000000000000000000000-2pyB`) + .option(`--secret-key `, `'Secret Key 1 or Secret Key 2' of Paypal OAuth2 app. For example: EH8KCXtew--000000000000000000000000000000000000000_C-1_5UP_000000000000000CB7KDp`) .option( - `--total [value]`, - `When set to false, the total count returned will be 0 and will not be calculated.`, + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, (value: string | undefined) => value === undefined ? true : parseBool(value), ) - .option(`--where `, `Filter using a simple comparison expression. Repeat for multiple filters. Supports field=value, field!=value, field>value, field>=value, field collectQueryValue(parseWhereQuery(value), previous)) - .option(`--sort-asc `, `Sort results by an attribute in ascending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) - .option(`--sort-desc `, `Sort results by an attribute in descending order. Repeat for multiple sort fields.`, (value: string, previous: string[] | undefined) => collectQueryValue(value, previous)) - .option(`--limit `, `Maximum number of results to return.`, parseInteger) - .option(`--offset `, `Number of results to skip.`, parseInteger) - .option(`--cursor-after `, `Return results after this cursor ID.`) - .option(`--cursor-before `, `Return results before this cursor ID.`) .action( actionRunner( - async ({ queries, total, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => - parse(await (await getProjectClient()).listKeys(buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), total)), + async ({ clientId, secretKey, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Paypal(clientId, secretKey, enabled)), ), ); -const projectCreateKeyCommand = project - .command(`create-key`) - .description(`Create a new API key. It's recommended to have multiple API keys with strict scopes for separate functions within your project.`) - .requiredOption(`--key-id `, `Key ID. Choose a custom ID or generate a random ID with \`ID.unique()\`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.`) - .requiredOption(`--name `, `Key name. Max length: 128 chars.`) - .requiredOption(`--scopes [scopes...]`, `Key scopes list. Maximum of 100 scopes are allowed.`) - .option(`--expire `, `Expiration time in ISO 8601 (https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.`) +const projectUpdateOAuth2PaypalSandboxCommand = project + .command(`update-o-auth-2-paypal-sandbox`) + .description(`Update the project OAuth2 PaypalSandbox configuration.`) + .option(`--client-id `, `'Client ID' of PaypalSandbox OAuth2 app. For example: AdhIEG7-000000000000-0000000000000000000000000000000-0000000000000000000000-2pyB`) + .option(`--secret-key `, `'Secret Key 1 or Secret Key 2' of PaypalSandbox OAuth2 app. For example: EH8KCXtew--000000000000000000000000000000000000000_C-1_5UP_000000000000000CB7KDp`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ keyId, name, scopes, expire }) => - parse(await (await getProjectClient()).createKey(keyId, name, scopes, expire)), + async ({ clientId, secretKey, enabled }) => + parse(await (await getProjectClient()).updateOAuth2PaypalSandbox(clientId, secretKey, enabled)), ), ); -const projectGetKeyCommand = project - .command(`get-key`) - .description(`Get a key by its unique ID. `) - .requiredOption(`--key-id `, `Key ID.`) +const projectUpdateOAuth2PodioCommand = project + .command(`update-o-auth-2-podio`) + .description(`Update the project OAuth2 Podio configuration.`) + .option(`--client-id `, `'Client ID' of Podio OAuth2 app. For example: appwrite-o0000000st-app`) + .option(`--client-secret `, `'Client Secret' of Podio OAuth2 app. For example: Rn247T0000000000000000000000000000000000000000000000000000W2zWTN`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ keyId }) => - parse(await (await getProjectClient()).getKey(keyId)), + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Podio(clientId, clientSecret, enabled)), ), ); -const projectUpdateKeyCommand = project - .command(`update-key`) - .description(`Update a key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key.`) - .requiredOption(`--key-id `, `Key ID.`) - .requiredOption(`--name `, `Key name. Max length: 128 chars.`) - .requiredOption(`--scopes [scopes...]`, `Key scopes list. Maximum of 100 scopes are allowed.`) - .option(`--expire `, `Expiration time in ISO 8601 (https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.`) +const projectUpdateOAuth2SalesforceCommand = project + .command(`update-o-auth-2-salesforce`) + .description(`Update the project OAuth2 Salesforce configuration.`) + .option(`--customer-key `, `'Consumer Key' of Salesforce OAuth2 app. For example: 3MVG9I0000000000000000000000000000000000000000000000000000000000000000000000000C5Aejq`) + .option(`--customer-secret `, `'Consumer Secret' of Salesforce OAuth2 app. For example: 3w000000000000e2`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ keyId, name, scopes, expire }) => - parse(await (await getProjectClient()).updateKey(keyId, name, scopes, expire)), + async ({ customerKey, customerSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Salesforce(customerKey, customerSecret, enabled)), ), ); -const projectDeleteKeyCommand = project - .command(`delete-key`) - .description(`Delete a key by its unique ID. Once deleted, the key can no longer be used to authenticate API calls.`) - .requiredOption(`--key-id `, `Key ID.`) +const projectUpdateOAuth2SlackCommand = project + .command(`update-o-auth-2-slack`) + .description(`Update the project OAuth2 Slack configuration.`) + .option(`--client-id `, `'Client ID' of Slack OAuth2 app. For example: 23000000089.15000000000023`) + .option(`--client-secret `, `'Client Secret' of Slack OAuth2 app. For example: 81656000000000000000000000f3d2fd`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ keyId }) => - parse(await (await getProjectClient()).deleteKey(keyId)), + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Slack(clientId, clientSecret, enabled)), ), ); -const projectUpdateLabelsCommand = project - .command(`update-labels`) - .description(`Update the project labels. Labels can be used to easily filter projects in an organization.`) - .requiredOption(`--labels [labels...]`, `Array of project labels. Replaces the previous labels. Maximum of 1000 labels are allowed, each up to 36 alphanumeric characters long.`) +const projectUpdateOAuth2SpotifyCommand = project + .command(`update-o-auth-2-spotify`) + .description(`Update the project OAuth2 Spotify configuration.`) + .option(`--client-id `, `'Client ID' of Spotify OAuth2 app. For example: 6ec271000000000000000000009beace`) + .option(`--client-secret `, `'Client Secret' of Spotify OAuth2 app. For example: db068a000000000000000000008b5b9f`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) .action( actionRunner( - async ({ labels }) => - parse(await (await getProjectClient()).updateLabels(labels)), + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Spotify(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2StripeCommand = project + .command(`update-o-auth-2-stripe`) + .description(`Update the project OAuth2 Stripe configuration.`) + .option(`--client-id `, `'Client ID' of Stripe OAuth2 app. For example: ca_UKibXX0000000000000000000006byvR`) + .option(`--api-secret-key `, `'API Secret Key' of Stripe OAuth2 app. For example: sk_51SfOd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000QGWYfp`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, apiSecretKey, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Stripe(clientId, apiSecretKey, enabled)), + ), + ); + + +const projectUpdateOAuth2TradeshiftCommand = project + .command(`update-o-auth-2-tradeshift`) + .description(`Update the project OAuth2 Tradeshift configuration.`) + .option(`--oauth-2-client-id `, `'OAuth2 Client ID' of Tradeshift OAuth2 app. For example: appwrite-tes00000.0000000000est-app`) + .option(`--oauth-2-client-secret `, `'OAuth2 Client Secret' of Tradeshift OAuth2 app. For example: 7cb52700-0000-0000-0000-000000ca5b83`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ oauth2ClientId, oauth2ClientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Tradeshift(oauth2ClientId, oauth2ClientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2TradeshiftSandboxCommand = project + .command(`update-o-auth-2-tradeshift-sandbox`) + .description(`Update the project OAuth2 Tradeshift Sandbox configuration.`) + .option(`--oauth-2-client-id `, `'OAuth2 Client ID' of Tradeshift Sandbox OAuth2 app. For example: appwrite-tes00000.0000000000est-app`) + .option(`--oauth-2-client-secret `, `'OAuth2 Client Secret' of Tradeshift Sandbox OAuth2 app. For example: 7cb52700-0000-0000-0000-000000ca5b83`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ oauth2ClientId, oauth2ClientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2TradeshiftSandbox(oauth2ClientId, oauth2ClientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2TwitchCommand = project + .command(`update-o-auth-2-twitch`) + .description(`Update the project OAuth2 Twitch configuration.`) + .option(`--client-id `, `'Client ID' of Twitch OAuth2 app. For example: vvi0in000000000000000000ikmt9p`) + .option(`--client-secret `, `'Client Secret' of Twitch OAuth2 app. For example: pmapue000000000000000000zylw3v`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Twitch(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2WordPressCommand = project + .command(`update-o-auth-2-word-press`) + .description(`Update the project OAuth2 WordPress configuration.`) + .option(`--client-id `, `'Client ID' of WordPress OAuth2 app. For example: 130005`) + .option(`--client-secret `, `'Client Secret' of WordPress OAuth2 app. For example: PlBfJS0000000000000000000000000000000000000000000000000000EdUZJk`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2WordPress(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2XCommand = project + .command(`update-o-auth-2x`) + .description(`Update the project OAuth2 X configuration.`) + .option(`--customer-key `, `'Customer Key' of X OAuth2 app. For example: slzZV0000000000000NFLaWT`) + .option(`--secret-key `, `'Secret Key' of X OAuth2 app. For example: tkEPkp00000000000000000000000000000000000000FTxbI9`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ customerKey, secretKey, enabled }) => + parse(await (await getProjectClient()).updateOAuth2X(customerKey, secretKey, enabled)), + ), + ); + + +const projectUpdateOAuth2YahooCommand = project + .command(`update-o-auth-2-yahoo`) + .description(`Update the project OAuth2 Yahoo configuration.`) + .option(`--client-id `, `'Client ID, also known as Customer Key' of Yahoo OAuth2 app. For example: dj0yJm000000000000000000000000000000000000000000000000000000000000000000000000000000000000Z4PWRm`) + .option(`--client-secret `, `'Client Secret, also known as Customer Secret' of Yahoo OAuth2 app. For example: cf978f0000000000000000000000000000c5e2e9`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Yahoo(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2YandexCommand = project + .command(`update-o-auth-2-yandex`) + .description(`Update the project OAuth2 Yandex configuration.`) + .option(`--client-id `, `'Client ID' of Yandex OAuth2 app. For example: 6a8a6a0000000000000000000091483c`) + .option(`--client-secret `, `'Client Secret' of Yandex OAuth2 app. For example: bbf98500000000000000000000c75a63`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Yandex(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2ZohoCommand = project + .command(`update-o-auth-2-zoho`) + .description(`Update the project OAuth2 Zoho configuration.`) + .option(`--client-id `, `'Client ID' of Zoho OAuth2 app. For example: 1000.83C178000000000000000000RPNX0B`) + .option(`--client-secret `, `'Client Secret' of Zoho OAuth2 app. For example: fb5cac000000000000000000000000000000a68f6e`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Zoho(clientId, clientSecret, enabled)), + ), + ); + + +const projectUpdateOAuth2ZoomCommand = project + .command(`update-o-auth-2-zoom`) + .description(`Update the project OAuth2 Zoom configuration.`) + .option(`--client-id `, `'Client ID' of Zoom OAuth2 app. For example: QMAC00000000000000w0AQ`) + .option(`--client-secret `, `'Client Secret' of Zoom OAuth2 app. For example: GAWsG4000000000000000000007U01ON`) + .option( + `--enabled [value]`, + `OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .action( + actionRunner( + async ({ clientId, clientSecret, enabled }) => + parse(await (await getProjectClient()).updateOAuth2Zoom(clientId, clientSecret, enabled)), + ), + ); + + +const projectGetOAuth2ProviderCommand = project + .command(`get-o-auth-2-provider`) + .description(`Get a single OAuth2 provider configuration. Credential fields (client secret, p8 file, key/team IDs) are write-only and always returned empty.`) + .requiredOption(`--provider `, `OAuth2 provider key. For example: github, google, apple.`) + .action( + actionRunner( + async ({ provider }) => + parse(await (await getProjectClient()).getOAuth2Provider(provider)), ), ); @@ -347,6 +1292,26 @@ const projectDeletePlatformCommand = project ); +const projectListPoliciesCommand = project + .command(`list-policies`) + .description(`Get a list of all project policies and their current configuration.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common pagination prefer --limit and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Only supported methods are limit and offset`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .action( + actionRunner( + async ({ queries, total, limit, offset }) => + parse(await (await getProjectClient()).listPolicies(buildQueries({ queries, limit, offset }), total)), + ), + ); + + const projectUpdateMembershipPrivacyPolicyCommand = project .command(`update-membership-privacy-policy`) .description(`Updating this policy allows you to control if team members can see other members information. When enabled, all team members can see ID, name, email, phone number, and MFA status of other members..`) @@ -486,6 +1451,18 @@ const projectUpdateUserLimitPolicyCommand = project ); +const projectGetPolicyCommand = project + .command(`get-policy`) + .description(`Get a policy by its unique ID. This endpoint returns the current configuration for the requested project policy.`) + .requiredOption(`--policy-id `, `Policy ID. Can be one of: password-dictionary, password-history, password-personal-data, session-alert, session-duration, session-invalidation, session-limit, user-limit, membership-privacy.`) + .action( + actionRunner( + async ({ policyId }) => + parse(await (await getProjectClient()).getPolicy(policyId)), + ), + ); + + const projectUpdateProtocolCommand = project .command(`update-protocol`) .description(`Update properties of a specific protocol. Use this endpoint to enable or disable a protocol in your project. `) @@ -550,6 +1527,26 @@ const projectCreateSMTPTestCommand = project ); +const projectListEmailTemplatesCommand = project + .command(`list-email-templates`) + .description(`Get a list of all custom email templates configured for the project. This endpoint returns an array of all configured email templates and their locales.`) + .option(`--queries [queries...]`, `Raw Appwrite JSON query strings (legacy). Use this for advanced queries or automation; for common pagination prefer --limit and --offset. When mixed, raw --queries are sent before generated flag queries. Array of query strings generated using the Query class provided by the SDK. Learn more about queries (https://appwrite.io/docs/queries). Only supported methods are limit and offset`) + .option( + `--total [value]`, + `When set to false, the total count returned will be 0 and will not be calculated.`, + (value: string | undefined) => + value === undefined ? true : parseBool(value), + ) + .option(`--limit `, `Maximum number of results to return.`, parseInteger) + .option(`--offset `, `Number of results to skip.`, parseInteger) + .action( + actionRunner( + async ({ queries, total, limit, offset }) => + parse(await (await getProjectClient()).listEmailTemplates(buildQueries({ queries, limit, offset }), total)), + ), + ); + + const projectUpdateEmailTemplateCommand = project .command(`update-email-template`) .description(`Update a custom email template for the specified locale and type. Use this endpoint to modify the content of your email templates.`) diff --git a/lib/commands/services/projects.ts b/lib/commands/services/projects.ts index 0bf931e9..cde6ce97 100644 --- a/lib/commands/services/projects.ts +++ b/lib/commands/services/projects.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Projects } from "@appwrite.io/console"; @@ -116,45 +117,6 @@ const projectsUpdateCommand = projects ); -const projectsDeleteCommand = projects - .command(`delete`) - .description(`Delete a project by its unique ID.`) - .requiredOption(`--project-id `, `Project unique ID.`) - .action( - actionRunner( - async ({ projectId }) => - parse(await (await getProjectsClient()).delete(projectId)), - ), - ); - - -const projectsUpdateMockNumbersCommand = projects - .command(`update-mock-numbers`) - .description(`Update the list of mock phone numbers for testing. Use these numbers to bypass SMS verification in development. `) - .requiredOption(`--project-id `, `Project unique ID.`) - .requiredOption(`--numbers [numbers...]`, `An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.`) - .action( - actionRunner( - async ({ projectId, numbers }) => - parse(await (await getProjectsClient()).updateMockNumbers(projectId, numbers)), - ), - ); - - -const projectsUpdateAuthStatusCommand = projects - .command(`update-auth-status`) - .description(`Update the status of a specific authentication method. Use this endpoint to enable or disable different authentication methods such as email, magic urls or sms in your project. `) - .requiredOption(`--project-id `, `Project unique ID.`) - .requiredOption(`--method `, `Auth Method. Possible values: email-password,magic-url,email-otp,anonymous,invites,jwt,phone`) - .requiredOption(`--status `, `Set the status of this auth method.`, parseBool) - .action( - actionRunner( - async ({ projectId, method, status }) => - parse(await (await getProjectsClient()).updateAuthStatus(projectId, method, status)), - ), - ); - - const projectsListDevKeysCommand = projects .command(`list-dev-keys`) .description(`List all the project\'s dev keys. Dev keys are project specific and allow you to bypass rate limits and get better error logging during development.'`) @@ -230,41 +192,6 @@ const projectsDeleteDevKeyCommand = projects ); -const projectsCreateJWTCommand = projects - .command(`create-jwt`) - .description(`Create a new JWT token. This token can be used to authenticate users with custom scopes and expiration time. `) - .requiredOption(`--project-id `, `Project unique ID.`) - .requiredOption(`--scopes [scopes...]`, `List of scopes allowed for JWT key. Maximum of 100 scopes are allowed.`) - .option(`--duration `, `Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.`, parseInteger) - .action( - actionRunner( - async ({ projectId, scopes, duration }) => - parse(await (await getProjectsClient()).createJWT(projectId, scopes, duration)), - ), - ); - - -const projectsUpdateOAuth2Command = projects - .command(`update-o-auth-2`) - .description(`Update the OAuth2 provider configurations. Use this endpoint to set up or update the OAuth2 provider credentials or enable/disable providers. `) - .requiredOption(`--project-id `, `Project unique ID.`) - .requiredOption(`--provider `, `Provider Name`) - .option(`--app-id `, `Provider app ID. Max length: 256 chars.`) - .option(`--secret `, `Provider secret key. Max length: 512 chars.`) - .option( - `--enabled [value]`, - `Provider status. Set to 'false' to disable new session creation.`, - (value: string | undefined) => - value === undefined ? true : parseBool(value), - ) - .action( - actionRunner( - async ({ projectId, provider, appId, secret, enabled }) => - parse(await (await getProjectsClient()).updateOAuth2(projectId, provider, appId, secret, enabled)), - ), - ); - - const projectsListSchedulesCommand = projects .command(`list-schedules`) .description(`Get a list of all the project's schedules. You can use the query params to filter your results.`) @@ -308,7 +235,7 @@ const projectsCreateScheduleCommand = projects .action( actionRunner( async ({ projectId, resourceType, resourceId, schedule, active, data }) => - parse(await (await getProjectsClient()).createSchedule(projectId, resourceType, resourceId, schedule, active, JSON.parse(data))), + parse(await (await getProjectsClient()).createSchedule(projectId, resourceType, resourceId, schedule, active, parseJsonObject(data, "--data"))), ), ); diff --git a/lib/commands/services/tables-db.ts b/lib/commands/services/tables-db.ts index 13ce8a94..eae8e72a 100644 --- a/lib/commands/services/tables-db.ts +++ b/lib/commands/services/tables-db.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { TablesDB } from "@appwrite.io/console"; @@ -1245,7 +1246,7 @@ const tablesDBCreateRowCommand = tablesDB .action( actionRunner( async ({ databaseId, tableId, rowId, data, permissions, transactionId }) => - parse(await (await getTablesDBClient()).createRow(databaseId, tableId, rowId, JSON.parse(data), permissions, transactionId)), + parse(await (await getTablesDBClient()).createRow(databaseId, tableId, rowId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); @@ -1299,7 +1300,7 @@ const tablesDBUpdateRowsCommand = tablesDB .action( actionRunner( async ({ databaseId, tableId, data, queries, transactionId, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }) => - parse(await (await getTablesDBClient()).updateRows(databaseId, tableId, JSON.parse(data), buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), transactionId)), + parse(await (await getTablesDBClient()).updateRows(databaseId, tableId, parseJsonObject(data, "--data"), buildQueries({ queries, where, sortAsc, sortDesc, cursorAfter, cursorBefore, limit, offset }), transactionId)), ), ); @@ -1355,7 +1356,7 @@ const tablesDBUpsertRowCommand = tablesDB .action( actionRunner( async ({ databaseId, tableId, rowId, data, permissions, transactionId }) => - parse(await (await getTablesDBClient()).upsertRow(databaseId, tableId, rowId, JSON.parse(data), permissions, transactionId)), + parse(await (await getTablesDBClient()).upsertRow(databaseId, tableId, rowId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); @@ -1372,7 +1373,7 @@ const tablesDBUpdateRowCommand = tablesDB .action( actionRunner( async ({ databaseId, tableId, rowId, data, permissions, transactionId }) => - parse(await (await getTablesDBClient()).updateRow(databaseId, tableId, rowId, JSON.parse(data), permissions, transactionId)), + parse(await (await getTablesDBClient()).updateRow(databaseId, tableId, rowId, parseJsonObject(data, "--data"), permissions, transactionId)), ), ); diff --git a/lib/commands/services/teams.ts b/lib/commands/services/teams.ts index dd2bad14..c502740e 100644 --- a/lib/commands/services/teams.ts +++ b/lib/commands/services/teams.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, hint, } from "../../parser.js"; import { Teams } from "@appwrite.io/console"; @@ -269,7 +270,7 @@ const teamsUpdatePrefsCommand = teams .action( actionRunner( async ({ teamId, prefs }) => - parse(await (await getTeamsClient()).updatePrefs(teamId, JSON.parse(prefs))), + parse(await (await getTeamsClient()).updatePrefs(teamId, parseJsonObject(prefs, "--prefs"))), ), ); diff --git a/lib/commands/services/users.ts b/lib/commands/services/users.ts index 6412fd1d..5dad94c4 100644 --- a/lib/commands/services/users.ts +++ b/lib/commands/services/users.ts @@ -12,6 +12,7 @@ import { parse, parseBool, parseInteger, + parseJsonObject, } from "../../parser.js"; import { Users } from "@appwrite.io/console"; @@ -499,7 +500,7 @@ const usersUpdatePrefsCommand = users .action( actionRunner( async ({ userId, prefs }) => - parse(await (await getUsersClient()).updatePrefs(userId, JSON.parse(prefs))), + parse(await (await getUsersClient()).updatePrefs(userId, parseJsonObject(prefs, "--prefs"))), ), ); diff --git a/lib/config.ts b/lib/config.ts index 6b5f6092..c3d7e74f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -38,9 +38,17 @@ import type { GlobalConfigData, } from "./types.js"; import { createSettingsObject } from "./utils.js"; -import { EXECUTABLE_NAME, TOP_LEVEL_RESOURCE_ARRAY_KEYS } from "./constants.js"; +import { + CONFIG_RESOURCE_KEYS, + EXECUTABLE_NAME, + TOP_LEVEL_RESOURCE_ARRAY_KEYS, +} from "./constants.js"; import { JSONBig } from "./json.js"; +type ConfigResourceKey = (typeof CONFIG_RESOURCE_KEYS)[number]; + +type ConfigIncludePaths = Partial>; + /** * Extract keys from a Zod object schema. * Handles both plain ZodObject and ZodEffects (schemas with refinements). @@ -76,6 +84,7 @@ const CONFIG_KEY_ORDER = [ "projectId", "projectName", "endpoint", + "includes", "settings", "functions", "sites", @@ -85,10 +94,302 @@ const CONFIG_KEY_ORDER = [ "tables", "buckets", "teams", + "webhooks", "topics", "messages", ]; +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === "object" && !Array.isArray(value); +} + +function ensureDirectoryForFile(filePath: string): void { + const dir = _path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +function assertValidIncludePath(resource: string, includePath: unknown): string { + if (typeof includePath !== "string" || includePath.trim() === "") { + throw new Error(`Config include for '${resource}' must be a file path.`); + } + + const normalizedPath = includePath.trim(); + if (normalizedPath.includes("\0")) { + throw new Error(`Config include '${resource}' cannot contain null bytes.`); + } + + if (normalizedPath.includes("#")) { + throw new Error( + `Config include '${resource}' must be a file path without a JSON pointer fragment.`, + ); + } + + if (normalizedPath.split(/[\\/]+/).includes("..")) { + throw new Error( + `Config include '${resource}' cannot contain parent directory segments.`, + ); + } + + if (_path.isAbsolute(normalizedPath)) { + throw new Error(`Config include '${resource}' must be a relative file path.`); + } + + if (/^[a-z][a-z0-9+.-]*:/i.test(normalizedPath)) { + throw new Error(`Config include '${resource}' must be a local file path.`); + } + + if (!normalizedPath.toLowerCase().endsWith(".json")) { + throw new Error(`Config include '${resource}' must point to a JSON file.`); + } + + return normalizedPath; +} + +function getConfigIncludePaths(data: Record): ConfigIncludePaths { + const includes = data.includes; + if (includes === undefined) { + return {}; + } + + if (!isRecord(includes)) { + throw new Error("Config 'includes' must be an object."); + } + + const includePaths: ConfigIncludePaths = {}; + for (const [key, value] of Object.entries(includes)) { + if (!TOP_LEVEL_RESOURCE_ARRAY_KEYS.has(key)) { + throw new Error(`Unsupported config include '${key}'.`); + } + includePaths[key as ConfigResourceKey] = assertValidIncludePath(key, value); + } + + return includePaths; +} + +function readJsonFile(filePath: string): T { + try { + return JSONBig.parse(fs.readFileSync(filePath, "utf8")); + } catch (error) { + const message = error instanceof Error ? error.message : "Unknown error"; + throw new Error(`Failed to read config file '${filePath}': ${message}`); + } +} + +function writeJsonFile(filePath: string, data: unknown): void { + ensureDirectoryForFile(filePath); + fs.writeFileSync(filePath, JSONBig.stringify(data, null, 4), { + mode: 0o600, + }); +} + +function resolveIncludePath(configFilePath: string, includePath: string): string { + return _path.resolve(_path.dirname(configFilePath), includePath); +} + +function isPathInside(parentPath: string, childPath: string): boolean { + const relativePath = _path.relative(parentPath, childPath); + return ( + relativePath === "" || + (!relativePath.startsWith("..") && !_path.isAbsolute(relativePath)) + ); +} + +function assertIncludePathInsideProject( + configFilePath: string, + resource: string, + includePath: string, +): string { + const resolvedPath = resolveIncludePath(configFilePath, includePath); + if (!isPathInside(_path.dirname(configFilePath), resolvedPath)) { + throw new Error( + `Config include '${resource}' must resolve inside the project directory.`, + ); + } + + return resolvedPath; +} + +function resolveConfigData( + configFilePath: string, + rootData: Record, + includePaths: ConfigIncludePaths, +): Record { + const resolvedData = { ...rootData }; + + for (const [resource, includePath] of Object.entries(includePaths)) { + if (rootData[resource] !== undefined) { + throw new Error( + `Config resource '${resource}' cannot be defined both inline and in includes.`, + ); + } + + const resolvedPath = assertIncludePathInsideProject( + configFilePath, + resource, + includePath, + ); + const includedData = fs.existsSync(resolvedPath) + ? readJsonFile(resolvedPath) + : []; + if (!Array.isArray(includedData)) { + throw new Error( + `Config include '${resource}' must point to a JSON file containing an array.`, + ); + } + + resolvedData[resource] = includedData; + } + + return resolvedData; +} + +export function readLocalConfigFile(filePath: string): ConfigType { + const rootData = readJsonFile>(filePath); + const includePaths = getConfigIncludePaths(rootData); + return resolveConfigData(filePath, rootData, includePaths) as ConfigType; +} + +export function getLocalConfigResourceDirname( + filePath: string, + resource: "functions" | "sites", +): string { + return getLocalConfigResourceDirnames(filePath)[resource]; +} + +export function getLocalConfigResourceDirnames(filePath: string): Record< + "functions" | "sites", + string +> { + const rootData = fs.existsSync(filePath) + ? readJsonFile>(filePath) + : {}; + const includePaths = getConfigIncludePaths(rootData); + + const getResourceDirname = (resource: "functions" | "sites"): string => { + const includePath = includePaths[resource]; + if (!includePath) { + return _path.dirname(filePath); + } + + return _path.dirname( + assertIncludePathInsideProject(filePath, resource, includePath), + ); + }; + + return { + functions: getResourceDirname("functions"), + sites: getResourceDirname("sites"), + }; +} + +export function resolveLocalConfigResourcePaths( + config: ConfigType, + filePath: string, +): ConfigType { + const rootData = fs.existsSync(filePath) + ? readJsonFile>(filePath) + : {}; + const includePaths = getConfigIncludePaths(rootData); + + const resolveResourcePath = ( + resource: "functions" | "sites", + resourcePath?: string, + ): string | undefined => { + if (!resourcePath || _path.isAbsolute(resourcePath)) { + return resourcePath; + } + + const includePath = includePaths[resource]; + const resourceDirectory = includePath + ? _path.dirname(resolveIncludePath(filePath, includePath)) + : _path.dirname(filePath); + return _path.resolve(resourceDirectory, resourcePath); + }; + + return { + ...config, + functions: config.functions?.map((func) => ({ + ...func, + path: resolveResourcePath("functions", func.path), + })), + sites: config.sites?.map((site) => ({ + ...site, + path: resolveResourcePath("sites", site.path), + })), + }; +} + +export function writeLocalConfigFile( + config: ConfigType, + filePath: string, +): void { + const rootData = fs.existsSync(filePath) + ? readJsonFile>(filePath) + : {}; + const includePaths = getConfigIncludePaths(rootData); + writeResolvedLocalConfig(config, filePath, rootData, includePaths); +} + +function writeResolvedLocalConfig( + config: Record, + filePath: string, + rootData: Record, + includePaths: ConfigIncludePaths, +): void { + const sanitizedData = pruneDeprecatedSiteFields(config); + const rootOutput: Record = { + ...rootData, + ...sanitizedData, + }; + + for (const [resource, includePath] of Object.entries(includePaths)) { + const resourceData = sanitizedData[resource] ?? []; + if (!Array.isArray(resourceData)) { + throw new Error(`Config resource '${resource}' must be an array.`); + } + + writeJsonFile( + assertIncludePathInsideProject(filePath, resource, includePath), + resourceData, + ); + delete rootOutput[resource]; + } + + if (Object.keys(includePaths).length > 0) { + rootOutput.includes = includePaths; + } else { + delete rootOutput.includes; + } + + writeJsonFile( + filePath, + orderConfigKeys(pruneEmptyTopLevelResourceArrays(rootOutput)), + ); +} + +function getWriteIncludePaths( + config: Record, + includePaths: ConfigIncludePaths, + includePathHints: ConfigIncludePaths, +): ConfigIncludePaths { + const writeIncludePaths: ConfigIncludePaths = { ...includePaths }; + + for (const resource of CONFIG_RESOURCE_KEYS) { + if (writeIncludePaths[resource] || !includePathHints[resource]) { + continue; + } + + const resourceData = config[resource]; + if (Array.isArray(resourceData) && resourceData.length > 0) { + writeIncludePaths[resource] = includePathHints[resource]; + } + } + + return writeIncludePaths; +} + function orderConfigKeys>(data: T): T { const ordered: Record = {}; @@ -175,10 +476,12 @@ class Config { readonly path: string; protected data: T; - constructor(path: string) { + constructor(path: string, autoRead = true) { this.path = path; this.data = {} as T; - this.read(); + if (autoRead) { + this.read(); + } } read(): void { @@ -293,6 +596,9 @@ class Config { class Local extends Config { static CONFIG_FILE_PATH = `${EXECUTABLE_NAME}.config.json`; static CONFIG_FILE_PATH_LEGACY = `${EXECUTABLE_NAME}.json`; + private rootData: Record = {}; + private includePaths: ConfigIncludePaths = {}; + private includePathHints: ConfigIncludePaths = {}; configDirectoryPath = ""; constructor( @@ -306,21 +612,65 @@ class Local extends Config { absolutePath = `${process.cwd()}/${path}`; } - super(absolutePath); + super(absolutePath, false); this.configDirectoryPath = _path.dirname(absolutePath); + this.read(); } - write(): void { - const dir = _path.dirname(this.path); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); + read(): void { + if (!fs.existsSync(this.path)) { + this.rootData = {}; + this.includePaths = {}; + this.includePathHints = {}; + this.data = {} as ConfigType; + return; } - const orderedData = orderConfigKeys( - pruneEmptyTopLevelResourceArrays(pruneDeprecatedSiteFields(this.data)), + + this.rootData = readJsonFile>(this.path); + this.includePaths = getConfigIncludePaths(this.rootData); + this.includePathHints = { + ...this.includePathHints, + ...this.includePaths, + }; + this.data = resolveConfigData( + this.path, + this.rootData, + this.includePaths, + ) as ConfigType; + } + + write(): void { + const writeIncludePaths = getWriteIncludePaths( + this.data as Record, + this.includePaths, + this.includePathHints, ); - fs.writeFileSync(this.path, JSONBig.stringify(orderedData, null, 4), { - mode: 0o600, - }); + writeResolvedLocalConfig( + this.data as Record, + this.path, + this.rootData ?? {}, + writeIncludePaths, + ); + this.includePathHints = { + ...this.includePathHints, + ...writeIncludePaths, + }; + this.read(); + } + + clear(): void { + for (const [resource, includePath] of Object.entries(this.includePaths)) { + writeJsonFile( + assertIncludePathInsideProject(this.path, resource, includePath), + [], + ); + } + + this.rootData = {}; + this.includePaths = {}; + this.includePathHints = {}; + this.data = {} as ConfigType; + writeJsonFile(this.path, {}); } static findConfigFile(filename: string): string | null { @@ -347,6 +697,23 @@ class Local extends Config { return _path.dirname(this.path); } + getResourceDirname(resource: ConfigResourceKey): string { + const includePath = this.includePaths[resource]; + if (!includePath) { + return this.getDirname(); + } + + return _path.dirname(resolveIncludePath(this.path, includePath)); + } + + resolveResourcePath(resource: ConfigResourceKey, resourcePath: string): string { + if (_path.isAbsolute(resourcePath)) { + return resourcePath; + } + + return _path.resolve(this.getResourceDirname(resource), resourcePath); + } + getEndpoint(): string { return this.get("endpoint") || ""; } diff --git a/lib/constants.ts b/lib/constants.ts index 06ffb350..8e7994c7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,7 +1,7 @@ // SDK export const SDK_TITLE = 'Appwrite'; export const SDK_TITLE_LOWER = 'appwrite'; -export const SDK_VERSION = '19.2.0'; +export const SDK_VERSION = '20.0.0'; export const SDK_NAME = 'Command Line'; export const SDK_PLATFORM = 'console'; export const SDK_LANGUAGE = 'cli'; diff --git a/lib/emulation/docker.ts b/lib/emulation/docker.ts index b1e8d69f..f764a21e 100644 --- a/lib/emulation/docker.ts +++ b/lib/emulation/docker.ts @@ -36,7 +36,7 @@ function getFunctionFiles(func: FunctionType): { files: string[]; ignorer: ignoreModule.Ignore; } { - const functionDir = path.join(localConfig.getDirname(), func.path); + const functionDir = localConfig.resolveResourcePath("functions", func.path); const ignorer = getFunctionIgnorer(func, functionDir); const files = getAllFiles(functionDir) .map((file) => path.relative(functionDir, file)) @@ -46,7 +46,7 @@ function getFunctionFiles(func: FunctionType): { } export function assertFunctionSourceCode(func: FunctionType): void { - const functionDir = path.join(localConfig.getDirname(), func.path); + const functionDir = localConfig.resolveResourcePath("functions", func.path); if (!fs.existsSync(functionDir)) { throw new Error( @@ -301,12 +301,7 @@ export async function dockerBuild( `Unable to build function '${func.name}'.`, ); - const copyPath = path.join( - localConfig.getDirname(), - func.path, - ".appwrite", - "build.tar.gz", - ); + const copyPath = path.join(functionDir, ".appwrite", "build.tar.gz"); const copyDir = path.dirname(copyPath); if (!fs.existsSync(copyDir)) { fs.mkdirSync(copyDir, { recursive: true }); @@ -342,11 +337,7 @@ export async function dockerBuild( } // Clean up temp files - const tempPath = path.join( - localConfig.getDirname(), - func.path, - "code.tar.gz", - ); + const tempPath = path.join(functionDir, "code.tar.gz"); if (fs.existsSync(tempPath)) { fs.rmSync(tempPath, { force: true }); } @@ -362,7 +353,7 @@ export async function dockerStart( port: number, ): Promise { // Pack function files - const functionDir = path.join(localConfig.getDirname(), func.path); + const functionDir = localConfig.resolveResourcePath("functions", func.path); const imageName = getRuntimeImageName(func); const runtimeName = func.runtime.split("-").slice(0, -1).join("-"); @@ -474,20 +465,13 @@ export async function dockerCleanup(functionId: string): Promise { await dockerStop(functionId); const func = localConfig.getFunction(functionId); - const appwritePath = path.join( - localConfig.getDirname(), - func.path, - ".appwrite", - ); + const functionDir = localConfig.resolveResourcePath("functions", func.path); + const appwritePath = path.join(functionDir, ".appwrite"); if (fs.existsSync(appwritePath)) { fs.rmSync(appwritePath, { recursive: true, force: true }); } - const tempPath = path.join( - localConfig.getDirname(), - func.path, - "code.tar.gz", - ); + const tempPath = path.join(functionDir, "code.tar.gz"); if (fs.existsSync(tempPath)) { fs.rmSync(tempPath, { force: true }); } diff --git a/lib/emulation/utils.ts b/lib/emulation/utils.ts index fd166c97..9b2145b3 100644 --- a/lib/emulation/utils.ts +++ b/lib/emulation/utils.ts @@ -1,8 +1,7 @@ import { EventEmitter } from "node:events"; -import { localConfig } from "../config.js"; import { log } from "../parser.js"; -import { sdkForConsole, sdkForProject } from "../sdks.js"; -import { Projects, Scopes, Users } from "@appwrite.io/console"; +import { sdkForProject } from "../sdks.js"; +import { Project, Scopes, Users } from "@appwrite.io/console"; export const openRuntimesVersion = "v5"; @@ -107,8 +106,8 @@ export const JwtManager = { userId: string | null = null, projectScopes: Scopes[] = [], ): Promise { - const consoleClient = await sdkForConsole(); - const projectsClient = new Projects(consoleClient); + const projectClient = await sdkForProject(); + const projectService = new Project(projectClient); if (this.timerWarn) { clearTimeout(this.timerWarn); @@ -138,7 +137,6 @@ export const JwtManager = { ); // 60 mins if (userId) { - const projectClient = await sdkForProject(); const usersClient = new Users(projectClient); await usersClient.get({ userId, @@ -150,12 +148,11 @@ export const JwtManager = { this.userJwt = userResponse.jwt; } - const functionResponse = await projectsClient.createJWT({ - projectId: localConfig.getProject().projectId!, + const functionResponse = await projectService.createEphemeralKey({ scopes: projectScopes, duration: 60 * 60, }); - this.functionJwt = functionResponse.jwt; + this.functionJwt = functionResponse.secret; }, }; diff --git a/lib/parser.ts b/lib/parser.ts index 5800df6a..18a417f7 100644 --- a/lib/parser.ts +++ b/lib/parser.ts @@ -762,6 +762,29 @@ export const parseBool = (value: string): boolean => { throw new InvalidArgumentError("Not a boolean."); }; +export const parseJsonObject = ( + value: string | undefined, + optionName: string, +): Record | undefined => { + if (value === undefined) { + return undefined; + } + + try { + const parsed = JSON.parse(value) as unknown; + + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + return parsed as Record; + } + } catch { + throw new InvalidArgumentError( + `${optionName} must be a valid JSON object.`, + ); + } + + throw new InvalidArgumentError(`${optionName} must be a valid JSON object.`); +}; + export const log = (message?: string): void => { console.log(`${chalk.cyan.bold("ℹ Info:")} ${chalk.cyan(message ?? "")}`); }; diff --git a/lib/utils.ts b/lib/utils.ts index 3196b66c..3e5e727d 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -55,7 +55,10 @@ export const createSettingsObject = (project: Models.Project): SettingsType => { passwordDictionary: project.authPasswordDictionary, personalDataCheck: project.authPersonalDataCheck, sessionAlerts: project.authSessionAlerts, - mockNumbers: project.authMockNumbers, + mockNumbers: project.authMockNumbers?.map((mockNumber) => ({ + phone: mockNumber.number, + otp: mockNumber.otp, + })), }, }, }; diff --git a/package-lock.json b/package-lock.json index f6ecc691..dea9b49d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "appwrite-cli", - "version": "19.2.0", + "version": "20.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "appwrite-cli", - "version": "19.2.0", + "version": "20.0.0", "license": "BSD-3-Clause", "dependencies": { - "@appwrite.io/console": "11.0.0", + "@appwrite.io/console": "12.0.0", "chalk": "4.1.2", "chokidar": "^3.6.0", "cli-progress": "^3.12.0", @@ -52,9 +52,9 @@ } }, "node_modules/@appwrite.io/console": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-11.0.0.tgz", - "integrity": "sha512-7w2MNVDP5cNLExJlEOttfBAvOFd1GA+rAUBoqhtvd81GLZZrMZck7QZv4cyhz3LO54jUg5zBWbyC1lsXCRWiEg==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@appwrite.io/console/-/console-12.0.0.tgz", + "integrity": "sha512-lvB4z53DvUh6zPRZP7F2nbhdu5WbdqA+cJO6aCqSjZ9egqETOKOrMl6M7UtmPO6TkxM2CGsDVNf5pYDECletqg==", "license": "BSD-3-Clause", "dependencies": { "json-bigint": "1.0.0" @@ -126,9 +126,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2137,17 +2137,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", - "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz", + "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/type-utils": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/type-utils": "8.59.1", + "@typescript-eslint/utils": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2160,22 +2160,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.0", + "@typescript-eslint/parser": "^8.59.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", - "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz", + "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3" }, "engines": { @@ -2191,14 +2191,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz", + "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", + "@typescript-eslint/tsconfig-utils": "^8.59.1", + "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "engines": { @@ -2213,14 +2213,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz", + "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2231,9 +2231,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz", + "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==", "dev": true, "license": "MIT", "engines": { @@ -2248,15 +2248,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", - "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz", + "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/utils": "8.59.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2273,9 +2273,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz", + "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==", "dev": true, "license": "MIT", "engines": { @@ -2287,16 +2287,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz", + "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.59.1", + "@typescript-eslint/tsconfig-utils": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2315,16 +2315,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz", + "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2339,13 +2339,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz", + "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2598,9 +2598,9 @@ } }, "node_modules/b4a": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", - "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -2663,9 +2663,9 @@ } }, "node_modules/bare-os": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.0.tgz", - "integrity": "sha512-JTjuZyNIDpw+GytMO4a6TK1VXdVKKJr6DRxEHasyuYyShV2deuiHJK/ahGZlebc+SG0/wJCB9XK8gprBGDFi/Q==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", + "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2683,9 +2683,9 @@ } }, "node_modules/bare-stream": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.0.tgz", - "integrity": "sha512-3zAJRZMDFGjdn+RVnNpF9kuELw+0Fl3lpndM4NcEOhb9zwtSo/deETfuIwMSE5BXanA0FrN1qVjffGwAg2Y7EA==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.1.tgz", + "integrity": "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5353,9 +5353,9 @@ "license": "MIT" }, "node_modules/node-abi": { - "version": "3.89.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", - "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "version": "3.90.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.90.0.tgz", + "integrity": "sha512-pZNQT7UnYlMwMBy5N1lV5X/YLTbZM5ncytN3xL7CHEzhDN8uVe0u55yaPUJICIJjaCW8NrM5BFdqr7HLweStNA==", "dev": true, "license": "MIT", "dependencies": { @@ -5645,12 +5645,12 @@ } }, "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", "license": "MIT", "dependencies": { - "@xmldom/xmldom": "^0.8.8", + "@xmldom/xmldom": "^0.9.10", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" }, @@ -6534,9 +6534,9 @@ } }, "node_modules/tar-stream": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", - "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", "dev": true, "license": "MIT", "dependencies": { @@ -6811,16 +6811,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", - "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz", + "integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.0", - "@typescript-eslint/parser": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0" + "@typescript-eslint/eslint-plugin": "8.59.1", + "@typescript-eslint/parser": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/utils": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7193,9 +7193,9 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 6281b495..0faaa5cb 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "type": "module", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API", - "version": "19.2.0", + "version": "20.0.0", "license": "BSD-3-Clause", "main": "dist/index.cjs", "module": "dist/index.js", @@ -51,7 +51,7 @@ "windows-arm64": "esbuild cli.ts --bundle --loader:.hbs=text --platform=node --target=node18 --format=esm --external:fsevents --external:terminal-image --outfile=dist/bundle-win-arm64.mjs && pkg dist/bundle-win-arm64.mjs -t node18-win-arm64 -o build/appwrite-cli-win-arm64.exe" }, "dependencies": { - "@appwrite.io/console": "11.0.0", + "@appwrite.io/console": "12.0.0", "chalk": "4.1.2", "chokidar": "^3.6.0", "cli-progress": "^3.12.0", diff --git a/scoop/appwrite.config.json b/scoop/appwrite.config.json index 9afc1c64..6af910da 100644 --- a/scoop/appwrite.config.json +++ b/scoop/appwrite.config.json @@ -1,12 +1,12 @@ { "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json", - "version": "19.2.0", + "version": "20.0.0", "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.", "homepage": "https://github.com/appwrite/sdk-for-cli", "license": "BSD-3-Clause", "architecture": { "64bit": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/19.2.0/appwrite-cli-win-x64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.0.0/appwrite-cli-win-x64.exe", "bin": [ [ "appwrite-cli-win-x64.exe", @@ -15,7 +15,7 @@ ] }, "arm64": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/19.2.0/appwrite-cli-win-arm64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/20.0.0/appwrite-cli-win-arm64.exe", "bin": [ [ "appwrite-cli-win-arm64.exe",