diff --git a/.eslintrc.json b/.eslintrc.json index 7f7f0c0a..6518ac8e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,7 @@ "sourceType": "module", "project": ["tsconfig.json", "tsconfig.test.json"] }, - "extends": ["@doist/eslint-config/recommended-requiring-type-checking"], + "extends": ["@doist/eslint-config/recommended-type-checked"], "env": { "browser": true, "jest": true diff --git a/.github/workflows/deploy-docusaurus.yml b/.github/workflows/deploy-docusaurus.yml index f4a5bef0..dcde7cec 100644 --- a/.github/workflows/deploy-docusaurus.yml +++ b/.github/workflows/deploy-docusaurus.yml @@ -3,7 +3,7 @@ name: Deploy Docs to Pages on: push: branches: - - v4 + - main workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages @@ -34,7 +34,10 @@ jobs: with: node-version-file: .nvmrc - - name: Install dependencies + - name: Install SDK dependencies + run: npm ci + + - name: Install docusaurus dependencies working-directory: ./website run: npm ci diff --git a/.gitignore b/.gitignore index 83f39b19..7b11d93f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules/ scratch.ts .vscode/ +.DS_Store \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..7f8f4fcf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# ignore auto-generated documentation website +website \ No newline at end of file diff --git a/README.md b/README.md index a0b42c34..084cae04 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,19 @@ api.getTasks() ### Documentation -For more detailed reference documentation, have a look at the [API documentation with TypeScript examples](https://developer.todoist.com/rest/v2/?javascript). +For more detailed reference documentation, have a look at the [Todoist API v1 Documentation](https://todoist.com/api/v1). + +### Migration Guide + +If you're migrating from an older version of the Todoist API (v9), please refer to the [official migration guide](https://todoist.com/api/v1/docs#tag/Migrating-from-v9) for detailed information about the changes and breaking updates. + +Key changes in v1 include: + +- Updated endpoint structure +- New pagination system +- Unified error response format +- Object renames (e.g., items → tasks, notes → comments) +- URL renames and endpoint signature changes ## Development and Testing diff --git a/jest.config.js b/jest.config.js index beb2540e..41177972 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { - preset: "ts-jest", + preset: 'ts-jest', transform: { '^.+\\.ts': 'ts-jest' }, testMatch: ['**/*.test.ts'], clearMocks: true, - testEnvironment: 'node' + testEnvironment: 'node', } diff --git a/package-lock.json b/package-lock.json index 4e5ba0a1..c0ecbde4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,46 +1,46 @@ { "name": "@doist/todoist-api-typescript", - "version": "3.0.3", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@doist/todoist-api-typescript", - "version": "3.0.2", + "version": "4.0.0", "license": "MIT", "dependencies": { "axios": "^1.0.0", "axios-case-converter": "^1.0.0", "axios-retry": "^3.1.9", - "runtypes": "^6.5.0", "ts-custom-error": "^3.2.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zod": "^3.24.1" }, "devDependencies": { - "@doist/eslint-config": "8.1.3", - "@doist/prettier-config": "3.0.5", + "@doist/eslint-config": "11.1.0", + "@doist/prettier-config": "4.0.0", "@types/jest": "29.4.0", "@types/uuid": "9.0.7", - "@typescript-eslint/eslint-plugin": "5.54.1", - "@typescript-eslint/parser": "5.54.1", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "eslint": "8.35.0", "eslint-config-prettier": "8.7.0", "eslint-import-resolver-webpack": "0.13.2", "eslint-plugin-import": "2.27.5", - "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-prettier": "5.1.3", "husky": "8.0.3", "jest": "29.5.0", "lint-staged": "13.1.4", "npm-run-all": "4.1.5", - "prettier": "2.8.4", + "prettier": "3.3.2", "rimraf": "3.0.2", "ts-jest": "29.0.5", "ts-node": "10.9.1", "type-fest": "^4.12.0", - "typescript": "4.9.5" + "typescript": "5.3.2" }, "peerDependencies": { - "type-fest": "^4.5.0" + "type-fest": "^4.12.0" } }, "node_modules/@babel/code-frame": { @@ -696,23 +696,35 @@ } }, "node_modules/@doist/eslint-config": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@doist/eslint-config/-/eslint-config-8.1.3.tgz", - "integrity": "sha512-KOTprpyplc3mVgVF+DYgCi0BjWWVSo7qlF4iQJ5/1Nq02NC1nsJLcifve0MK9rrh7u/eYIA8RslosBgJ/TLFeA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@doist/eslint-config/-/eslint-config-11.1.0.tgz", + "integrity": "sha512-dVrHyADEcReruUVxUcuz7pmQa7uWM+HidJOJe7rs95eW53XGmHI+D/igqTUzW2tANzAeWJHy0QYzR5CxaWqxWQ==", "dev": true, "dependencies": { "@doist/eslint-plugin-doist": "1.0.0", - "eslint-config-prettier": "^8.5.0" + "eslint-config-prettier": "^8.8.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.33.0 || ^5.16.0", - "@typescript-eslint/parser": "^4.33.0 || ^5.16.0", - "eslint": "^7.32.0 || ^8.11.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.11.0", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-simple-import-sort": "^7.0.0 || ^8.0.0" + "eslint-plugin-simple-import-sort": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^12.0.0" + } + }, + "node_modules/@doist/eslint-config/node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, "node_modules/@doist/eslint-plugin-doist": { @@ -731,12 +743,51 @@ } }, "node_modules/@doist/prettier-config": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@doist/prettier-config/-/prettier-config-3.0.5.tgz", - "integrity": "sha512-QvMAKaXkg3Jib3VL/2yxFk7sHlUpHm3IsdaBEoDEvBGJL4Y8TtPl3Yvi3Cam9bV5yiuDJbA+Co3NDRMA4iAxog==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@doist/prettier-config/-/prettier-config-4.0.0.tgz", + "integrity": "sha512-wp3DL0qFciDk63O8jTKGX+xPwpliq+0532ODIXcsBVm65aJrzvPlrUM2BeOrSKiLEbWnTELRboVebBaDedLJuA==", "dev": true, "peerDependencies": { - "prettier": "^2.0.0" + "prettier": "^3.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { @@ -1260,6 +1311,18 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.23", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz", @@ -1350,9 +1413,9 @@ } }, "node_modules/@types/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "peer": true, "dependencies": { @@ -1422,9 +1485,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -1446,9 +1509,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1479,32 +1542,33 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", - "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/type-utils": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1513,25 +1577,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", - "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1540,16 +1605,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", - "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1557,25 +1622,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", - "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1584,12 +1649,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", - "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1597,21 +1662,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", - "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1623,43 +1689,66 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", - "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", - "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1667,12 +1756,15 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@webassemblyjs/ast": { @@ -2233,12 +2325,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3108,21 +3200,30 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } @@ -3232,6 +3333,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -3487,6 +3589,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -3565,22 +3668,22 @@ "dev": true }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -3629,9 +3732,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -3963,6 +4066,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4090,9 +4199,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -5742,18 +5851,6 @@ "tslib": "^2.0.3" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5824,12 +5921,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -5894,12 +5991,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -6468,15 +6559,15 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -6819,11 +6910,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/runtypes": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/runtypes/-/runtypes-6.5.1.tgz", - "integrity": "sha512-vYxcAYzC868ZY4BgazBomT9dpWHZnG3CH++5mhlVKGKqf2MzON4itmEmQt3uGTUOyipeUn7GALAN0nQhjPNRtA==" - }, "node_modules/rxjs": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", @@ -6873,13 +6959,10 @@ } }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -7251,6 +7334,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tapable": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", @@ -7389,6 +7488,18 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-custom-error": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.2.0.tgz", @@ -7517,30 +7628,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-check": { "version": "0.4.0", @@ -7590,16 +7680,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -7902,17 +7992,14 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yaml": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz", - "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -7964,6 +8051,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -8461,13 +8556,22 @@ } }, "@doist/eslint-config": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@doist/eslint-config/-/eslint-config-8.1.3.tgz", - "integrity": "sha512-KOTprpyplc3mVgVF+DYgCi0BjWWVSo7qlF4iQJ5/1Nq02NC1nsJLcifve0MK9rrh7u/eYIA8RslosBgJ/TLFeA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@doist/eslint-config/-/eslint-config-11.1.0.tgz", + "integrity": "sha512-dVrHyADEcReruUVxUcuz7pmQa7uWM+HidJOJe7rs95eW53XGmHI+D/igqTUzW2tANzAeWJHy0QYzR5CxaWqxWQ==", "dev": true, "requires": { "@doist/eslint-plugin-doist": "1.0.0", - "eslint-config-prettier": "^8.5.0" + "eslint-config-prettier": "^8.8.0" + }, + "dependencies": { + "eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "requires": {} + } } }, "@doist/eslint-plugin-doist": { @@ -8480,12 +8584,35 @@ } }, "@doist/prettier-config": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@doist/prettier-config/-/prettier-config-3.0.5.tgz", - "integrity": "sha512-QvMAKaXkg3Jib3VL/2yxFk7sHlUpHm3IsdaBEoDEvBGJL4Y8TtPl3Yvi3Cam9bV5yiuDJbA+Co3NDRMA4iAxog==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@doist/prettier-config/-/prettier-config-4.0.0.tgz", + "integrity": "sha512-wp3DL0qFciDk63O8jTKGX+xPwpliq+0532ODIXcsBVm65aJrzvPlrUM2BeOrSKiLEbWnTELRboVebBaDedLJuA==", "dev": true, "requires": {} }, + "@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } + } + }, + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, "@eslint/eslintrc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", @@ -8907,6 +9034,12 @@ "fastq": "^1.6.0" } }, + "@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true + }, "@sinclair/typebox": { "version": "0.25.23", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz", @@ -8997,9 +9130,9 @@ } }, "@types/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "peer": true, "requires": { @@ -9069,9 +9202,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/json5": { @@ -9093,9 +9226,9 @@ "dev": true }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "@types/stack-utils": { @@ -9126,108 +9259,130 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz", - "integrity": "sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/type-utils": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz", - "integrity": "sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz", - "integrity": "sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" } }, "@typescript-eslint/type-utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz", - "integrity": "sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.54.1", - "@typescript-eslint/utils": "5.54.1", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.1.tgz", - "integrity": "sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz", - "integrity": "sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/visitor-keys": "5.54.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "@typescript-eslint/utils": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.1.tgz", - "integrity": "sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.1", - "@typescript-eslint/types": "5.54.1", - "@typescript-eslint/typescript-estree": "5.54.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.54.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz", - "integrity": "sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "requires": { - "@typescript-eslint/types": "5.54.1", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "dependencies": { "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true } } @@ -9704,12 +9859,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browserslist": { @@ -10487,12 +10642,13 @@ } }, "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" } }, "eslint-plugin-react": { @@ -10576,6 +10732,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -10659,7 +10816,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "dev": true, + "peer": true }, "esutils": { "version": "2.0.3", @@ -10717,22 +10875,22 @@ "dev": true }, "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" } }, "fast-json-stable-stringify": { @@ -10775,9 +10933,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -11012,6 +11170,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -11097,9 +11261,9 @@ "dev": true }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "import-fresh": { @@ -12308,15 +12472,6 @@ "tslib": "^2.0.3" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -12374,12 +12529,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -12429,12 +12584,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -12861,9 +13010,9 @@ "dev": true }, "prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true }, "prettier-linter-helpers": { @@ -13100,11 +13249,6 @@ "queue-microtask": "^1.2.2" } }, - "runtypes": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/runtypes/-/runtypes-6.5.1.tgz", - "integrity": "sha512-vYxcAYzC868ZY4BgazBomT9dpWHZnG3CH++5mhlVKGKqf2MzON4itmEmQt3uGTUOyipeUn7GALAN0nQhjPNRtA==" - }, "rxjs": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", @@ -13144,13 +13288,10 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true }, "serialize-javascript": { "version": "6.0.0", @@ -13436,6 +13577,16 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "requires": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + } + }, "tapable": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", @@ -13541,6 +13692,13 @@ "is-number": "^7.0.0" } }, + "ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "requires": {} + }, "ts-custom-error": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.2.0.tgz", @@ -13613,26 +13771,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "type-check": { "version": "0.4.0", @@ -13667,9 +13808,9 @@ } }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true }, "unbox-primitive": { @@ -13908,16 +14049,10 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yaml": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz", - "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true }, "yargs": { @@ -13952,6 +14087,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==" } } } diff --git a/package.json b/package.json index e11d9a44..9d42417f 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "@doist/todoist-api-typescript", - "version": "3.0.3", + "version": "4.0.0", "description": "A typescript wrapper for the Todoist REST API.", "author": "Doist developers", "repository": "git@github.com:doist/todoist-api-typescript.git", - "homepage": "https://developer.todoist.com/rest/v1/?javascript", + "homepage": "https://doist.github.io/todoist-api-typescript/", "license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -27,32 +27,32 @@ "axios": "^1.0.0", "axios-case-converter": "^1.0.0", "axios-retry": "^3.1.9", - "runtypes": "^6.5.0", "ts-custom-error": "^3.2.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zod": "^3.24.1" }, "devDependencies": { - "@doist/eslint-config": "8.1.3", - "@doist/prettier-config": "3.0.5", + "@doist/eslint-config": "11.1.0", + "@doist/prettier-config": "4.0.0", "@types/jest": "29.4.0", "@types/uuid": "9.0.7", - "@typescript-eslint/eslint-plugin": "5.54.1", - "@typescript-eslint/parser": "5.54.1", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "eslint": "8.35.0", "eslint-config-prettier": "8.7.0", "eslint-import-resolver-webpack": "0.13.2", "eslint-plugin-import": "2.27.5", - "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-prettier": "5.1.3", "husky": "8.0.3", "jest": "29.5.0", "lint-staged": "13.1.4", "npm-run-all": "4.1.5", - "prettier": "2.8.4", + "prettier": "3.3.2", "rimraf": "3.0.2", "ts-jest": "29.0.5", "ts-node": "10.9.1", "type-fest": "^4.12.0", - "typescript": "4.9.5" + "typescript": "5.3.2" }, "peerDependencies": { "type-fest": "^4.12.0" diff --git a/src/TodoistApi.comments.test.ts b/src/TodoistApi.comments.test.ts index 5aa61597..12693dc2 100644 --- a/src/TodoistApi.comments.test.ts +++ b/src/TodoistApi.comments.test.ts @@ -5,9 +5,13 @@ import { COMMENT_WITH_OPTIONALS_AS_NULL_TASK, DEFAULT_AUTH_TOKEN, DEFAULT_COMMENT, + DEFAULT_RAW_COMMENT, DEFAULT_REQUEST_ID, + RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL, + RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT, + RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK, } from './testUtils/testDefaults' -import { getRestBaseUri, ENDPOINT_REST_COMMENTS } from './consts/endpoints' +import { getSyncBaseUri, ENDPOINT_REST_COMMENTS } from './consts/endpoints' import { setupRestClientMock } from './testUtils/mocks' function getTarget() { @@ -17,8 +21,11 @@ function getTarget() { describe('TodoistApi comment endpoints', () => { describe('getComments', () => { test('calls get request with expected params', async () => { - const getCommentsArgs = { projectId: '12' } - const requestMock = setupRestClientMock([DEFAULT_COMMENT]) + const getCommentsArgs = { projectId: '12', limit: 10, cursor: '0' } + const requestMock = setupRestClientMock({ + results: [DEFAULT_RAW_COMMENT], + nextCursor: '123', + }) const api = getTarget() await api.getComments(getCommentsArgs) @@ -26,7 +33,7 @@ describe('TodoistApi comment endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_COMMENTS, DEFAULT_AUTH_TOKEN, getCommentsArgs, @@ -34,25 +41,32 @@ describe('TodoistApi comment endpoints', () => { }) test('returns result from rest client', async () => { + const mockResponses = [ + DEFAULT_RAW_COMMENT, + RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK, + RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT, + RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL, + ] const expectedComments = [ DEFAULT_COMMENT, COMMENT_WITH_OPTIONALS_AS_NULL_TASK, COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT, COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL, ] - setupRestClientMock(expectedComments) + setupRestClientMock({ results: mockResponses, nextCursor: '123' }) const api = getTarget() - const comments = await api.getComments({ taskId: '12' }) + const { results: comments, nextCursor } = await api.getComments({ taskId: '12' }) expect(comments).toEqual(expectedComments) + expect(nextCursor).toBe('123') }) }) describe('getComment', () => { test('calls get on expected url', async () => { const commentId = '1' - const requestMock = setupRestClientMock(DEFAULT_COMMENT) + const requestMock = setupRestClientMock(DEFAULT_RAW_COMMENT) const api = getTarget() await api.getComment(commentId) @@ -60,20 +74,19 @@ describe('TodoistApi comment endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_COMMENTS}/${commentId}`, DEFAULT_AUTH_TOKEN, ) }) test('returns result from rest client', async () => { - const expectedComment = DEFAULT_COMMENT - setupRestClientMock(expectedComment) + setupRestClientMock(DEFAULT_RAW_COMMENT) const api = getTarget() const comment = await api.getComment('1') - expect(comment).toEqual(expectedComment) + expect(comment).toEqual(DEFAULT_COMMENT) }) }) @@ -84,7 +97,7 @@ describe('TodoistApi comment endpoints', () => { } test('makes post request with expected params', async () => { - const requestMock = setupRestClientMock(DEFAULT_COMMENT) + const requestMock = setupRestClientMock(DEFAULT_RAW_COMMENT) const api = getTarget() await api.addComment(addCommentArgs, DEFAULT_REQUEST_ID) @@ -92,7 +105,7 @@ describe('TodoistApi comment endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_COMMENTS, DEFAULT_AUTH_TOKEN, addCommentArgs, @@ -101,13 +114,12 @@ describe('TodoistApi comment endpoints', () => { }) test('returns result from rest client', async () => { - const expectedComment = DEFAULT_COMMENT - setupRestClientMock(expectedComment) + setupRestClientMock(DEFAULT_RAW_COMMENT) const api = getTarget() const comment = await api.addComment(addCommentArgs) - expect(comment).toEqual(expectedComment) + expect(comment).toEqual(DEFAULT_COMMENT) }) }) @@ -118,7 +130,7 @@ describe('TodoistApi comment endpoints', () => { test('makes post request with expected params', async () => { const taskId = '1' - const requestMock = setupRestClientMock(DEFAULT_COMMENT, 204) + const requestMock = setupRestClientMock(DEFAULT_RAW_COMMENT, 204) const api = getTarget() await api.updateComment(taskId, updateCommentArgs, DEFAULT_REQUEST_ID) @@ -126,7 +138,7 @@ describe('TodoistApi comment endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_COMMENTS}/${taskId}`, DEFAULT_AUTH_TOKEN, updateCommentArgs, @@ -135,13 +147,14 @@ describe('TodoistApi comment endpoints', () => { }) test('returns success result from rest client', async () => { - const returnedComment = { ...DEFAULT_COMMENT, ...updateCommentArgs } + const returnedComment = { ...DEFAULT_RAW_COMMENT, content: updateCommentArgs.content } + const expectedComment = { ...DEFAULT_COMMENT, content: updateCommentArgs.content } setupRestClientMock(returnedComment, 204) const api = getTarget() const result = await api.updateComment('1', updateCommentArgs) - expect(result).toEqual(returnedComment) + expect(result).toEqual(expectedComment) }) }) @@ -156,7 +169,7 @@ describe('TodoistApi comment endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'DELETE', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_COMMENTS}/${taskId}`, DEFAULT_AUTH_TOKEN, undefined, diff --git a/src/TodoistApi.labels.test.ts b/src/TodoistApi.labels.test.ts index 42a42e74..61d06172 100644 --- a/src/TodoistApi.labels.test.ts +++ b/src/TodoistApi.labels.test.ts @@ -1,6 +1,6 @@ import { TodoistApi } from '.' import { DEFAULT_AUTH_TOKEN, DEFAULT_LABEL, DEFAULT_REQUEST_ID } from './testUtils/testDefaults' -import { getRestBaseUri, ENDPOINT_REST_LABELS } from './consts/endpoints' +import { getSyncBaseUri, ENDPOINT_REST_LABELS } from './consts/endpoints' import { setupRestClientMock } from './testUtils/mocks' function getTarget() { @@ -19,7 +19,7 @@ describe('TodoistApi label endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_LABELS}/${labelId}`, DEFAULT_AUTH_TOKEN, ) @@ -37,28 +37,39 @@ describe('TodoistApi label endpoints', () => { describe('getLabels', () => { test('calls get on labels endpoint', async () => { - const requestMock = setupRestClientMock([DEFAULT_LABEL]) + const requestMock = setupRestClientMock({ + results: [DEFAULT_LABEL], + nextCursor: '123', + }) const api = getTarget() - await api.getLabels() + await api.getLabels({ limit: 10, cursor: '0' }) expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_LABELS, DEFAULT_AUTH_TOKEN, + { + limit: 10, + cursor: '0', + }, ) }) test('returns result from rest client', async () => { const labels = [DEFAULT_LABEL] - setupRestClientMock(labels) + setupRestClientMock({ + results: [DEFAULT_LABEL], + nextCursor: '123', + }) const api = getTarget() - const response = await api.getLabels() + const { results, nextCursor } = await api.getLabels() - expect(response).toEqual(labels) + expect(results).toEqual(labels) + expect(nextCursor).toBe('123') }) }) @@ -76,7 +87,7 @@ describe('TodoistApi label endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_LABELS, DEFAULT_AUTH_TOKEN, DEFAULT_ADD_LABEL_ARGS, @@ -109,7 +120,7 @@ describe('TodoistApi label endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_LABELS}/${labelId}`, DEFAULT_AUTH_TOKEN, DEFAULT_UPDATE_LABEL_ARGS, @@ -139,7 +150,7 @@ describe('TodoistApi label endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'DELETE', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_LABELS}/${labelId}`, DEFAULT_AUTH_TOKEN, undefined, diff --git a/src/TodoistApi.projects.test.ts b/src/TodoistApi.projects.test.ts index cc07b692..112aff58 100644 --- a/src/TodoistApi.projects.test.ts +++ b/src/TodoistApi.projects.test.ts @@ -7,7 +7,7 @@ import { PROJECT_WITH_OPTIONALS_AS_NULL, } from './testUtils/testDefaults' import { - getRestBaseUri, + getSyncBaseUri, ENDPOINT_REST_PROJECTS, ENDPOINT_REST_PROJECT_COLLABORATORS, } from './consts/endpoints' @@ -29,7 +29,7 @@ describe('TodoistApi project endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_PROJECTS}/${projectId}`, DEFAULT_AUTH_TOKEN, ) @@ -47,28 +47,34 @@ describe('TodoistApi project endpoints', () => { describe('getProjects', () => { test('calls get on projects endpoint', async () => { - const requestMock = setupRestClientMock([DEFAULT_PROJECT]) + const requestMock = setupRestClientMock({ + results: [DEFAULT_PROJECT], + nextCursor: '123', + }) const api = getTarget() - await api.getProjects() + const args = { limit: 10, cursor: '0' } + await api.getProjects(args) expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_PROJECTS, DEFAULT_AUTH_TOKEN, + args, ) }) test('returns result from rest client', async () => { const projects = [DEFAULT_PROJECT, PROJECT_WITH_OPTIONALS_AS_NULL] - setupRestClientMock(projects) + setupRestClientMock({ results: projects, nextCursor: '123' }) const api = getTarget() - const response = await api.getProjects() + const { results, nextCursor } = await api.getProjects() - expect(response).toEqual(projects) + expect(results).toEqual(projects) + expect(nextCursor).toBe('123') }) }) @@ -86,7 +92,7 @@ describe('TodoistApi project endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_PROJECTS, DEFAULT_AUTH_TOKEN, DEFAULT_ADD_PROJECT_ARGS, @@ -117,7 +123,7 @@ describe('TodoistApi project endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_PROJECTS}/${projectId}`, DEFAULT_AUTH_TOKEN, updateArgs, @@ -126,7 +132,7 @@ describe('TodoistApi project endpoints', () => { }) test('returns success result from rest client', async () => { - const returnedProject = { ...DEFAULT_PROJECT, DEFAULT_UPDATE_PROJECT_ARGS } + const returnedProject = { ...DEFAULT_PROJECT, ...DEFAULT_UPDATE_PROJECT_ARGS } setupRestClientMock(returnedProject, 204) const api = getTarget() @@ -147,7 +153,7 @@ describe('TodoistApi project endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'DELETE', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_PROJECTS}/${projectId}`, DEFAULT_AUTH_TOKEN, undefined, @@ -170,27 +176,30 @@ describe('TodoistApi project endpoints', () => { const users = [DEFAULT_USER] test('calls get on expected endpoint', async () => { - const requestMock = setupRestClientMock(users) + const requestMock = setupRestClientMock({ results: users, nextCursor: '123' }) const api = getTarget() - await api.getProjectCollaborators(projectId) + const args = { limit: 10, cursor: '0' } + await api.getProjectCollaborators(projectId, args) expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_PROJECTS}/${projectId}/${ENDPOINT_REST_PROJECT_COLLABORATORS}`, DEFAULT_AUTH_TOKEN, + args, ) }) test('returns result from rest client', async () => { - setupRestClientMock(users) + setupRestClientMock({ results: users, nextCursor: '123' }) const api = getTarget() - const returnedUsers = await api.getProjectCollaborators(projectId) + const { results, nextCursor } = await api.getProjectCollaborators(projectId) - expect(returnedUsers).toEqual(users) + expect(results).toEqual(users) + expect(nextCursor).toBe('123') }) }) }) diff --git a/src/TodoistApi.sections.test.ts b/src/TodoistApi.sections.test.ts index ec4f2119..0068ee1b 100644 --- a/src/TodoistApi.sections.test.ts +++ b/src/TodoistApi.sections.test.ts @@ -1,6 +1,6 @@ import { TodoistApi } from '.' import { DEFAULT_AUTH_TOKEN, DEFAULT_REQUEST_ID, DEFAULT_SECTION } from './testUtils/testDefaults' -import { getRestBaseUri, ENDPOINT_REST_SECTIONS } from './consts/endpoints' +import { getSyncBaseUri, ENDPOINT_REST_SECTIONS } from './consts/endpoints' import { setupRestClientMock } from './testUtils/mocks' function getTarget() { @@ -19,7 +19,7 @@ describe('TodoistApi section endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_SECTIONS}/${sectionId}`, DEFAULT_AUTH_TOKEN, ) @@ -38,29 +38,34 @@ describe('TodoistApi section endpoints', () => { describe('getSections', () => { test('calls get on sections endpoint', async () => { const projectId = '123' - const requestMock = setupRestClientMock([DEFAULT_SECTION]) + const requestMock = setupRestClientMock({ + results: [DEFAULT_SECTION], + nextCursor: '123', + }) const api = getTarget() - await api.getSections(projectId) + const args = { projectId, limit: 10, cursor: '0' } + await api.getSections(args) expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_SECTIONS, DEFAULT_AUTH_TOKEN, - { projectId }, + args, ) }) test('returns result from rest client', async () => { const sections = [DEFAULT_SECTION] - setupRestClientMock(sections) + setupRestClientMock({ results: sections, nextCursor: '123' }) const api = getTarget() - const response = await api.getSections() + const { results, nextCursor } = await api.getSections({ projectId: '123' }) - expect(response).toEqual(sections) + expect(results).toEqual(sections) + expect(nextCursor).toBe('123') }) }) @@ -79,7 +84,7 @@ describe('TodoistApi section endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_SECTIONS, DEFAULT_AUTH_TOKEN, DEFAULT_ADD_SECTION_ARGS, @@ -110,7 +115,7 @@ describe('TodoistApi section endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_SECTIONS}/${sectionId}`, DEFAULT_AUTH_TOKEN, DEFAULT_UPDATE_SECTION_ARGS, @@ -140,7 +145,7 @@ describe('TodoistApi section endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'DELETE', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_SECTIONS}/${sectionId}`, DEFAULT_AUTH_TOKEN, undefined, diff --git a/src/TodoistApi.tasks.test.ts b/src/TodoistApi.tasks.test.ts index 886f7a89..a4c5a7c8 100644 --- a/src/TodoistApi.tasks.test.ts +++ b/src/TodoistApi.tasks.test.ts @@ -9,11 +9,11 @@ import { TASK_WITH_OPTIONALS_AS_NULL, } from './testUtils/testDefaults' import { - getRestBaseUri, getSyncBaseUri, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_REST_TASKS, + ENDPOINT_REST_TASKS_FILTER, ENDPOINT_SYNC_QUICK_ADD, } from './consts/endpoints' import { setupRestClientMock } from './testUtils/mocks' @@ -41,7 +41,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_TASKS, DEFAULT_AUTH_TOKEN, DEFAULT_ADD_TASK_ARGS, @@ -58,7 +58,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri('https://staging.todoist.com'), + getSyncBaseUri('https://staging.todoist.com'), ENDPOINT_REST_TASKS, DEFAULT_AUTH_TOKEN, DEFAULT_ADD_TASK_ARGS, @@ -89,7 +89,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_TASKS}/${taskId}`, DEFAULT_AUTH_TOKEN, DEFAULT_UPDATE_TASK_ARGS, @@ -119,7 +119,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_TASKS}/${taskId}/${ENDPOINT_REST_TASK_CLOSE}`, DEFAULT_AUTH_TOKEN, undefined, @@ -148,7 +148,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'POST', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_TASKS}/${taskId}/${ENDPOINT_REST_TASK_REOPEN}`, DEFAULT_AUTH_TOKEN, undefined, @@ -177,7 +177,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'DELETE', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_TASKS}/${taskId}`, DEFAULT_AUTH_TOKEN, undefined, @@ -243,7 +243,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), `${ENDPOINT_REST_TASKS}/${taskId}`, DEFAULT_AUTH_TOKEN, ) @@ -253,10 +253,15 @@ describe('TodoistApi task endpoints', () => { describe('getTasks', () => { const DEFAULT_GET_TASKS_ARGS = { projectId: '123', + limit: 10, + cursor: '0', } test('calls get on expected endpoint with args', async () => { - const requestMock = setupRestClientMock([DEFAULT_TASK, TASK_WITH_OPTIONALS_AS_NULL]) + const requestMock = setupRestClientMock({ + results: [DEFAULT_TASK, TASK_WITH_OPTIONALS_AS_NULL], + nextCursor: '123', + }) const api = getTarget() await api.getTasks(DEFAULT_GET_TASKS_ARGS) @@ -264,7 +269,7 @@ describe('TodoistApi task endpoints', () => { expect(requestMock).toBeCalledTimes(1) expect(requestMock).toBeCalledWith( 'GET', - getRestBaseUri(), + getSyncBaseUri(), ENDPOINT_REST_TASKS, DEFAULT_AUTH_TOKEN, DEFAULT_GET_TASKS_ARGS, @@ -273,12 +278,58 @@ describe('TodoistApi task endpoints', () => { test('returns result from rest client', async () => { const tasks = [DEFAULT_TASK] - setupRestClientMock(tasks) + setupRestClientMock({ results: tasks, nextCursor: '123' }) + const api = getTarget() + + const { results, nextCursor } = await api.getTasks(DEFAULT_GET_TASKS_ARGS) + + expect(results).toEqual(tasks) + expect(nextCursor).toBe('123') + }) + }) + + describe('getTasksByFilter', () => { + const DEFAULT_GET_TASKS_BY_FILTER_ARGS = { + query: 'today', + lang: 'en', + cursor: null, + limit: 10, + } + + test('calls get request with expected url', async () => { + const requestMock = setupRestClientMock({ results: [DEFAULT_TASK], nextCursor: null }) const api = getTarget() - const response = await api.getTasks(DEFAULT_GET_TASKS_ARGS) + await api.getTasksByFilter(DEFAULT_GET_TASKS_BY_FILTER_ARGS) + + expect(requestMock).toBeCalledTimes(1) + expect(requestMock).toBeCalledWith( + 'GET', + getSyncBaseUri(), + ENDPOINT_REST_TASKS_FILTER, + DEFAULT_AUTH_TOKEN, + DEFAULT_GET_TASKS_BY_FILTER_ARGS, + ) + }) + + test('returns result from rest client', async () => { + setupRestClientMock({ results: [DEFAULT_TASK], nextCursor: null }) + const api = getTarget() + + const response = await api.getTasksByFilter(DEFAULT_GET_TASKS_BY_FILTER_ARGS) + + expect(response).toEqual({ + results: [DEFAULT_TASK], + nextCursor: null, + }) + }) + + test('validates task array in response', async () => { + const invalidTask = { ...DEFAULT_TASK, due: '2020-01-31' } + setupRestClientMock({ results: [invalidTask], nextCursor: null }) + const api = getTarget() - expect(response).toEqual(tasks) + await expect(api.getTasksByFilter(DEFAULT_GET_TASKS_BY_FILTER_ARGS)).rejects.toThrow() }) }) }) diff --git a/src/TodoistApi.ts b/src/TodoistApi.ts index d48977a3..a2778e9d 100644 --- a/src/TodoistApi.ts +++ b/src/TodoistApi.ts @@ -1,13 +1,5 @@ -import { String } from 'runtypes' -import { - Task, - QuickAddTaskResponse, - Project, - Label, - User, - Section, - Comment, -} from './types/entities' +import { Project, Label, Section, Comment } from './types/entities' +import type { Task } from './types/entities' import { AddCommentArgs, AddLabelArgs, @@ -17,6 +9,7 @@ import { GetProjectCommentsArgs, GetTaskCommentsArgs, GetTasksArgs, + GetTasksByFilterArgs, UpdateCommentArgs, UpdateLabelArgs, UpdateProjectArgs, @@ -26,13 +19,26 @@ import { GetSharedLabelsArgs, RenameSharedLabelArgs, RemoveSharedLabelArgs, + GetProjectsArgs, + GetProjectCollaboratorsArgs, + GetLabelsArgs, + GetLabelsResponse, + GetTasksResponse, + GetProjectsResponse, + GetProjectCollaboratorsResponse, + GetSectionsArgs, + GetSectionsResponse, + GetSharedLabelsResponse, + GetCommentsResponse, + QuickAddTaskResponse, + type MoveTaskArgs, } from './types/requests' import { request, isSuccess } from './restClient' import { getTaskFromQuickAddResponse } from './utils/taskConverters' import { - getRestBaseUri, getSyncBaseUri, ENDPOINT_REST_TASKS, + ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_PROJECTS, ENDPOINT_SYNC_QUICK_ADD, ENDPOINT_REST_TASK_CLOSE, @@ -44,6 +50,7 @@ import { ENDPOINT_REST_LABELS_SHARED, ENDPOINT_REST_LABELS_SHARED_RENAME, ENDPOINT_REST_LABELS_SHARED_REMOVE, + ENDPOINT_SYNC, } from './consts/endpoints' import { validateComment, @@ -58,6 +65,13 @@ import { validateTaskArray, validateUserArray, } from './utils/validators' +import { z } from 'zod' + +import { v4 as uuidv4 } from 'uuid' +import { SyncResponse, type Command, type SyncRequest } from './types/sync' +import { TodoistRequestError } from './types' + +const MAX_COMMAND_COUNT = 100 /** * Joins path segments using `/` separator. @@ -68,24 +82,56 @@ function generatePath(...segments: string[]): string { return segments.join('/') } +/** + * A client for interacting with the Todoist API v1. + * This class provides methods to manage tasks, projects, sections, labels, and comments in Todoist. + * + * @example + * ```typescript + * const api = new TodoistApi('your-api-token'); + * + * // Get all tasks + * const tasks = await api.getTasks(); + * + * // Create a new task + * const newTask = await api.addTask({ + * content: 'My new task', + * projectId: '12345' + * }); + * ``` + * + * For more information about the Todoist API v1, see the [official documentation](https://todoist.com/api/v1). + * If you're migrating from v9, please refer to the [migration guide](https://todoist.com/api/v1/docs#tag/Migrating-from-v9). + */ export class TodoistApi { - authToken: string + private authToken: string + private syncApiBase: string - constructor(authToken: string, baseUrl?: string) { + constructor( + /** + * Your Todoist API token. + */ + authToken: string, + /** + * Optional custom API base URL. If not provided, defaults to Todoist's standard API endpoint + */ + baseUrl?: string, + ) { this.authToken = authToken - - this.restApiBase = getRestBaseUri(baseUrl) this.syncApiBase = getSyncBaseUri(baseUrl) } - private restApiBase: string - private syncApiBase: string - + /** + * Retrieves a single active (non-completed) task by its ID. + * + * @param id - The unique identifier of the task. + * @returns A promise that resolves to the requested task. + */ async getTask(id: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'GET', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_TASKS, id), this.authToken, ) @@ -93,22 +139,63 @@ export class TodoistApi { return validateTask(response.data) } - async getTasks(args?: GetTasksArgs): Promise { - const response = await request( + /** + * Retrieves a list of active tasks filtered by specific parameters. + * + * @param args - Filter parameters such as project ID, label ID, or due date. + * @returns A promise that resolves to an array of tasks. + */ + async getTasks(args: GetTasksArgs = {}): Promise { + const { + data: { results, nextCursor }, + } = await request( 'GET', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_TASKS, this.authToken, args, ) - return validateTaskArray(response.data) + return { + results: validateTaskArray(results), + nextCursor, + } } + /** + * Retrieves tasks filtered by a filter string. + * + * @param args - Parameters for filtering tasks, including the query string and optional language. + * @returns A promise that resolves to a paginated response of tasks. + */ + async getTasksByFilter(args: GetTasksByFilterArgs): Promise { + const { + data: { results, nextCursor }, + } = await request( + 'GET', + this.syncApiBase, + ENDPOINT_REST_TASKS_FILTER, + this.authToken, + args, + ) + + return { + results: validateTaskArray(results), + nextCursor, + } + } + + /** + * Creates a new task with the provided parameters. + * + * @param args - Task creation parameters such as content, due date, or priority. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the created task. + */ async addTask(args: AddTaskArgs, requestId?: string): Promise { const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_TASKS, this.authToken, args, @@ -118,6 +205,12 @@ export class TodoistApi { return validateTask(response.data) } + /** + * Quickly adds a task using natural language processing for due dates. + * + * @param args - Quick add task parameters, including content and due date. + * @returns A promise that resolves to the created task. + */ async quickAddTask(args: QuickAddTaskArgs): Promise { const response = await request( 'POST', @@ -132,11 +225,19 @@ export class TodoistApi { return validateTask(task) } + /** + * Updates an existing task by its ID with the provided parameters. + * + * @param id - The unique identifier of the task to update. + * @param args - Update parameters such as content, priority, or due date. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the updated task. + */ async updateTask(id: string, args: UpdateTaskArgs, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_TASKS, id), this.authToken, args, @@ -145,11 +246,79 @@ export class TodoistApi { return validateTask(response.data) } + /** + * Moves existing tasks by their ID to either a different parent/section/project. + * + * @param ids - The unique identifier of the tasks to be moved. + * @param args - The paramets that should contain only one of projectId, sectionId, or parentId + * @param requestId - Optional unique identifier for idempotency. + * @returns - A promise that resolves to an array of the updated tasks. + */ + async moveTasks(ids: string[], args: MoveTaskArgs, requestId?: string): Promise { + if (ids.length > MAX_COMMAND_COUNT) { + throw new TodoistRequestError(`Maximum number of items is ${MAX_COMMAND_COUNT}`, 400) + } + const uuid = uuidv4() + const commands: Command[] = ids.map((id) => ({ + type: 'item_move', + uuid, + args: { + id, + ...(args.projectId && { project_id: args.projectId }), + ...(args.sectionId && { section_id: args.sectionId }), + ...(args.parentId && { parent_id: args.parentId }), + }, + })) + + const syncRequest: SyncRequest = { + commands, + resource_types: ['items'], + } + + const response = await request( + 'POST', + this.syncApiBase, + ENDPOINT_SYNC, + this.authToken, + syncRequest, + requestId, + /*hasSyncCommands: */ true, + ) + + if (response.data.sync_status) { + Object.entries(response.data.sync_status).forEach(([_, value]) => { + if (value === 'ok') return + + throw new TodoistRequestError(value.error, value.http_code, value.error_extra) + }) + } + + if (!response.data.items?.length) { + throw new TodoistRequestError('Tasks not found', 404) + } + + const syncTasks = response.data.items.filter((task) => ids.includes(task.id)) + if (!syncTasks.length) { + throw new TodoistRequestError('Tasks not found', 404) + } + + const tasks = syncTasks.map(getTaskFromQuickAddResponse) + + return validateTaskArray(tasks) + } + + /** + * Closes (completes) a task by its ID. + * + * @param id - The unique identifier of the task to close. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to `true` if successful. + */ async closeTask(id: string, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_CLOSE), this.authToken, undefined, @@ -158,11 +327,18 @@ export class TodoistApi { return isSuccess(response) } + /** + * Reopens a previously closed (completed) task by its ID. + * + * @param id - The unique identifier of the task to reopen. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to `true` if successful. + */ async reopenTask(id: string, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_REOPEN), this.authToken, undefined, @@ -171,11 +347,18 @@ export class TodoistApi { return isSuccess(response) } + /** + * Deletes a task by its ID. + * + * @param id - The unique identifier of the task to delete. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to `true` if successful. + */ async deleteTask(id: string, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'DELETE', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_TASKS, id), this.authToken, undefined, @@ -184,11 +367,17 @@ export class TodoistApi { return isSuccess(response) } + /** + * Retrieves a project by its ID. + * + * @param id - The unique identifier of the project. + * @returns A promise that resolves to the requested project. + */ async getProject(id: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'GET', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_PROJECTS, id), this.authToken, ) @@ -196,21 +385,40 @@ export class TodoistApi { return validateProject(response.data) } - async getProjects(): Promise { - const response = await request( + /** + * Retrieves all projects with optional filters. + * + * @param args - Optional filters for retrieving projects. + * @returns A promise that resolves to an array of projects. + */ + async getProjects(args: GetProjectsArgs = {}): Promise { + const { + data: { results, nextCursor }, + } = await request( 'GET', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_PROJECTS, this.authToken, + args, ) - return validateProjectArray(response.data) + return { + results: validateProjectArray(results), + nextCursor, + } } + /** + * Creates a new project with the provided parameters. + * + * @param args - Project creation parameters such as name or color. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the created project. + */ async addProject(args: AddProjectArgs, requestId?: string): Promise { const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_PROJECTS, this.authToken, args, @@ -220,11 +428,19 @@ export class TodoistApi { return validateProject(response.data) } + /** + * Updates an existing project by its ID with the provided parameters. + * + * @param id - The unique identifier of the project to update. + * @param args - Update parameters such as name or color. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the updated project. + */ async updateProject(id: string, args: UpdateProjectArgs, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_PROJECTS, id), this.authToken, args, @@ -233,11 +449,18 @@ export class TodoistApi { return validateProject(response.data) } + /** + * Deletes a project by its ID. + * + * @param id - The unique identifier of the project to delete. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to `true` if successful. + */ async deleteProject(id: string, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'DELETE', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_PROJECTS, id), this.authToken, undefined, @@ -246,35 +469,68 @@ export class TodoistApi { return isSuccess(response) } - async getProjectCollaborators(projectId: string): Promise { - String.check(projectId) - const response = await request( + /** + * Retrieves a list of collaborators for a specific project. + * + * @param projectId - The unique identifier of the project. + * @param args - Optional parameters to filter collaborators. + * @returns A promise that resolves to an array of collaborators for the project. + */ + async getProjectCollaborators( + projectId: string, + args: GetProjectCollaboratorsArgs = {}, + ): Promise { + z.string().parse(projectId) + const { + data: { results, nextCursor }, + } = await request( 'GET', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_PROJECTS, projectId, ENDPOINT_REST_PROJECT_COLLABORATORS), this.authToken, + args, ) - return validateUserArray(response.data) + return { + results: validateUserArray(results), + nextCursor, + } } - async getSections(projectId?: string): Promise { - const response = await request( + /** + * Retrieves all sections within a specific project or matching criteria. + * + * @param args - Filter parameters such as project ID. + * @returns A promise that resolves to an array of sections. + */ + async getSections(args: GetSectionsArgs): Promise { + const { + data: { results, nextCursor }, + } = await request( 'GET', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_SECTIONS, this.authToken, - projectId ? { projectId } : undefined, + args, ) - return validateSectionArray(response.data) + return { + results: validateSectionArray(results), + nextCursor, + } } + /** + * Retrieves a single section by its ID. + * + * @param id - The unique identifier of the section. + * @returns A promise that resolves to the requested section. + */ async getSection(id: string): Promise
{ - String.check(id) + z.string().parse(id) const response = await request
( 'GET', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_SECTIONS, id), this.authToken, ) @@ -282,10 +538,17 @@ export class TodoistApi { return validateSection(response.data) } + /** + * Creates a new section within a project. + * + * @param args - Section creation parameters such as name or project ID. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the created section. + */ async addSection(args: AddSectionArgs, requestId?: string): Promise
{ const response = await request
( 'POST', - this.restApiBase, + this.syncApiBase, ENDPOINT_REST_SECTIONS, this.authToken, args, @@ -295,11 +558,19 @@ export class TodoistApi { return validateSection(response.data) } + /** + * Updates a section by its ID with the provided parameters. + * + * @param id - The unique identifier of the section to update. + * @param args - Update parameters such as name or project ID. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to the updated section. + */ async updateSection(id: string, args: UpdateSectionArgs, requestId?: string): Promise
{ - String.check(id) + z.string().parse(id) const response = await request( 'POST', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_SECTIONS, id), this.authToken, args, @@ -308,11 +579,18 @@ export class TodoistApi { return validateSection(response.data) } + /** + * Deletes a section by its ID. + * + * @param id - The unique identifier of the section to delete. + * @param requestId - Optional unique identifier for idempotency. + * @returns A promise that resolves to `true` if successful. + */ async deleteSection(id: string, requestId?: string): Promise { - String.check(id) + z.string().parse(id) const response = await request( 'DELETE', - this.restApiBase, + this.syncApiBase, generatePath(ENDPOINT_REST_SECTIONS, id), this.authToken, undefined, @@ -322,13 +600,16 @@ export class TodoistApi { } /** - * Fetches a personal label + * Retrieves a label by its ID. + * + * @param id - The unique identifier of the label. + * @returns A promise that resolves to the requested label. */ async getLabel(id: string): Promise