diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ed723b35..00000000 --- a/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": [ - "@babel/preset-env", - "@babel/preset-typescript" - ], - "plugins" : [ - "babel-plugin-replace-ts-export-assignment" - ] -} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..43fd5a73 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,15 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Development", + "image": "mcr.microsoft.com/devcontainers/typescript-node:latest", + "features": { + "ghcr.io/devcontainers/features/node:1": {} + }, + "postCreateCommand": "yarn install", + "customizations": { + "vscode": { + "extensions": ["esbenp.prettier-vscode"] + } + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e34f886d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,100 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Check types + run: ./scripts/lint + + build: + timeout-minutes: 5 + name: build + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Check build + run: ./scripts/build + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/imagekit-typescript' + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: github.repository == 'stainless-sdks/imagekit-typescript' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + + - name: Upload MCP Server tarball + if: github.repository == 'stainless-sdks/imagekit-typescript' + env: + URL: https://pkg.stainless.com/s?subpackage=mcp-server + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + BASE_PATH: packages/mcp-server + run: ./scripts/utils/upload-artifact.sh + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Build + run: ./scripts/build + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index 7263c90c..00000000 --- a/.github/workflows/nodejs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Node CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x, 14.x, 16.x, 18.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: Test and report coverage - run: | - npm i -g yarn - yarn install - yarn test - yarn test-e2e - # yarn report-coverage - env: - CI: true diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml deleted file mode 100644 index e1b73f85..00000000 --- a/.github/workflows/npmpublish.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Publish - -on: - release: - types: [published] - - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x, 14.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install, build, and test - run: | - npm i -g yarn - yarn install - yarn test - yarn test-e2e - env: - CI: true - - publish: - needs: build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 12 - registry-url: https://registry.npmjs.org/ - - name: yarn publish - run: | - npm i -g yarn - yarn config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN - yarn install - yarn publish - env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} - CI: true diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 00000000..678e0297 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,20 @@ +name: Release Doctor +on: + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'imagekit-developer/imagekit-nodejs' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: diff --git a/.gitignore b/.gitignore index 919ac523..74cba895 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,11 @@ +.prism.log node_modules -.vscode -.nyc_output -coverage.lcov -coverage -.DS_Store -dist* -tests/e2e/node-js/package.json -tests/e2e/node-js/yarn.lock -tests/e2e/typescript/package.json -tests/e2e/typescript/yarn.lock -tests/e2e/typescript/index.js -.cache yarn-error.log -*.tgz \ No newline at end of file +codegen.log +Brewfile.lock.json +dist +dist-deno +/*.tgz +.idea/ +dist-bundle +*.mcpb diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index fe5128ba..00000000 --- a/.mocharc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extension": ["ts"], - "spec": "tests/*.js", - "require": "babel-register.js" -} \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index eaa22bd7..00000000 --- a/.npmignore +++ /dev/null @@ -1,19 +0,0 @@ -.github -tests -sample -.npmignore -.babelrc -coverage -babel-register.js -.nyc_output -.vscode -.DS_Store -.mocharc.json -.nycrc -libs/* -utils/* -tsconfig* -fixup.sh -index.ts -*.tgz -test-e2e.sh \ No newline at end of file diff --git a/.nycrc b/.nycrc deleted file mode 100644 index be4975e7..00000000 --- a/.nycrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "check-coverage": true, - "lines": 95 -} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..7cc13dd1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +CHANGELOG.md +/ecosystem-tests/*/** +/node_modules +/deno + +# don't format tsc output, will break source maps +dist diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..af75adaf --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "arrowParens": "always", + "experimentalTernaries": true, + "printWidth": 110, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..aeda91d8 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "7.0.0" +} diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..e1604c7a --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 42 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/imagekit-inc%2Fimagekit-d1a3e6dfc45ae832b6b14a0aef25878985c679fa9f48c1470df188b1578ba648.yml +openapi_spec_hash: 1d382866fce3284f26d341f112988d9d +config_hash: ff23f46fe08ef3f43c57c8cf13eff3a1 diff --git a/Brewfile b/Brewfile new file mode 100644 index 00000000..e4feee60 --- /dev/null +++ b/Brewfile @@ -0,0 +1 @@ +brew "node" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ec52687..5d4f73e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,94 @@ -## Changelog +# Changelog -### SDK Version 6.0.0 +## 7.0.0 (2025-09-21) -### Breaking changes +Full Changelog: [v0.0.1-alpha.0...v7.0.0](https://github.com/imagekit-developer/imagekit-nodejs/compare/v0.0.1-alpha.0...v7.0.0) -**1. `listFiles` API response type** -* The `listFiles` method now returns a unified response type, ListFileResponse, which is an array of both `FileObject` and `FolderObject`. Previously, the response contained only `FileObject`. The `type` property in the response object indicates whether the object is a file or a folder. Even though this change has been made to just the type of the return object, it can be considered a breaking change so it may require require any code relying on the `listFiles` response to be updated. +### Features -``` -const result = await imagekit.listFiles({ skip: 0, limit: 10 }); +* add examples for URL generation and transformations in README ([4bad591](https://github.com/imagekit-developer/imagekit-nodejs/commit/4bad5917155a54e60ed5cbdfd10f1c1e98e14842)) +* add url signing and test cases ([b1594d8](https://github.com/imagekit-developer/imagekit-nodejs/commit/b1594d8e0e416811bb7a87e3d14492725dc1b2d4)) +* add webhook verification section to README with example code ([b28d7b3](https://github.com/imagekit-developer/imagekit-nodejs/commit/b28d7b376c90cf21704870cdd7c9401c86bac21d)) +* allow file parameter in FileUploadParams to accept string type for HTTP URL base base64 case. ([738f6d9](https://github.com/imagekit-developer/imagekit-nodejs/commit/738f6d9ef6649d4c9288c2d05f083b2ca9211ee7)) +* **api:** add ai-auto-description field with status options to components schema ([96c640d](https://github.com/imagekit-developer/imagekit-nodejs/commit/96c640d86b1810a122c8ba6418dd157cd0e1ff2d)) +* **api:** add BaseWebhookEvent ([dac30e1](https://github.com/imagekit-developer/imagekit-nodejs/commit/dac30e1479b5f022c0d59c0bd84ee928ba676dd2)) +* **api:** add new webhook events for upload transformations to enhance event tracking ([dd98040](https://github.com/imagekit-developer/imagekit-nodejs/commit/dd9804078ee46a656f8423de2845482bdaca6be8)) +* **api:** add signed URL options with expiration settings to enhance security features ([55d2dd1](https://github.com/imagekit-developer/imagekit-nodejs/commit/55d2dd18b0c717a5ede4fea09523098d806e87af)) +* **api:** extract UpdateFileDetailsRequest to model ([30d976b](https://github.com/imagekit-developer/imagekit-nodejs/commit/30d976b95ae76c09bc9152badd6bed801bf3cf57)) +* **api:** manual updates ([608ef99](https://github.com/imagekit-developer/imagekit-nodejs/commit/608ef9945b576180c3786380262b1a4074bef456)) +* **api:** manual updates ([d0d45ee](https://github.com/imagekit-developer/imagekit-nodejs/commit/d0d45ee5351438651649153cb70ed8d9809078a1)) +* **api:** manual updates ([78f9507](https://github.com/imagekit-developer/imagekit-nodejs/commit/78f9507314187a0a925eb095168caa5759b7d42b)) +* **api:** manual updates ([af5fd2f](https://github.com/imagekit-developer/imagekit-nodejs/commit/af5fd2f465f9d00d4822e1fac77cbe323094bd33)) +* **api:** manual updates ([2ac7656](https://github.com/imagekit-developer/imagekit-nodejs/commit/2ac76564b2119db0d9d4eddb399ddf422ecc6eba)) +* **api:** manual updates ([d208673](https://github.com/imagekit-developer/imagekit-nodejs/commit/d208673da821f783d8279d6eb22bfe1e41ee4f62)) +* **api:** manual updates ([76f3ed7](https://github.com/imagekit-developer/imagekit-nodejs/commit/76f3ed799e69105013d849c0d94de6778ab4da7a)) +* **api:** manual updates ([01bdaa0](https://github.com/imagekit-developer/imagekit-nodejs/commit/01bdaa02fe0d6a5d5fcdca09edd31d4562031ca7)) +* **api:** manual updates ([9d913fa](https://github.com/imagekit-developer/imagekit-nodejs/commit/9d913fa2de488eed9e5be5d4ec10b5ad83335c62)) +* **api:** manual updates ([dc932e3](https://github.com/imagekit-developer/imagekit-nodejs/commit/dc932e36e7d79742e2d1d39a8a4aaa7b667b85c1)) +* **api:** manual updates ([50c8520](https://github.com/imagekit-developer/imagekit-nodejs/commit/50c8520ab96f5e96dcb50ca3964be1f21acd1dec)) +* **api:** manual updates ([1d0423a](https://github.com/imagekit-developer/imagekit-nodejs/commit/1d0423a6b3866f9ad2cf65a09d0e9f902930c37e)) +* **api:** manual updates ([64fc454](https://github.com/imagekit-developer/imagekit-nodejs/commit/64fc45473e4072df18cff73024bcd4469258bf65)) +* **api:** manual updates ([f70d1c2](https://github.com/imagekit-developer/imagekit-nodejs/commit/f70d1c2fc248efb16b990e047796bf7aab5387c4)) +* **api:** manual updates ([4efbfee](https://github.com/imagekit-developer/imagekit-nodejs/commit/4efbfee0ca0de866a0ad77c607d7d6fb14a05c84)) +* **api:** manual updates ([174eee8](https://github.com/imagekit-developer/imagekit-nodejs/commit/174eee861dac548093cc6b561eb59496cb5539cb)) +* **api:** manual updates ([1b740df](https://github.com/imagekit-developer/imagekit-nodejs/commit/1b740dfb1e21293568614f5a7fe96468762f5286)) +* **api:** manual updates ([636a5a9](https://github.com/imagekit-developer/imagekit-nodejs/commit/636a5a991e4e648da2d183a6492e9a959938b2ec)) +* **api:** manual updates ([c1bc59b](https://github.com/imagekit-developer/imagekit-nodejs/commit/c1bc59ba35af6b0e7bac82e1e87e3937eda72cf1)) +* **api:** manual updates ([4d7286a](https://github.com/imagekit-developer/imagekit-nodejs/commit/4d7286a5b61168b8bccd44e2cf754938e63c8568)) +* **api:** manual updates ([8986981](https://github.com/imagekit-developer/imagekit-nodejs/commit/898698108afffb5ecffda06765b7c02c21f2e74c)) +* **api:** manual updates ([693e3cf](https://github.com/imagekit-developer/imagekit-nodejs/commit/693e3cf68ccd5a8de740ed35b9d0cc2660e88521)) +* **api:** manual updates ([ace1909](https://github.com/imagekit-developer/imagekit-nodejs/commit/ace190977c46f6702597fb4d6ea54133346724a2)) +* **api:** remove Stainless attribution from readme ([454c722](https://github.com/imagekit-developer/imagekit-nodejs/commit/454c7225ad3fbfda4f6807a0655b5d0b430b16d8)) +* **api:** update api docs link ([34d2eb1](https://github.com/imagekit-developer/imagekit-nodejs/commit/34d2eb1c9de598e7f01156588a8f942dc36f8a70)) +* **api:** Update env var name ([70c98e0](https://github.com/imagekit-developer/imagekit-nodejs/commit/70c98e08925b1884713e524129227003af75c7b6)) +* **docs:** add URL generation examples and authentication parameters to README ([7a2bc8f](https://github.com/imagekit-developer/imagekit-nodejs/commit/7a2bc8f71d50a730fa7ebf634d2775c30d21171f)) +* **docs:** improve descriptions for private API key and password fields in client settings ([7ab6b37](https://github.com/imagekit-developer/imagekit-nodejs/commit/7ab6b37f00f0b4ecba52bd4814370d22c5264c7e)) +* **helper:** implement getAuthenticationParameters method and test cases ([297bb95](https://github.com/imagekit-developer/imagekit-nodejs/commit/297bb95dabb0ed878bd009e1878b418ed26bf31e)) +* implement serializeUploadOptions function for upload option serialization and add tests ([cfce32f](https://github.com/imagekit-developer/imagekit-nodejs/commit/cfce32f9b706a52035714714dcbc8429e4072f04)) +* remove password field from ImageKit client initialization in tests and documentation ([08a5744](https://github.com/imagekit-developer/imagekit-nodejs/commit/08a5744777862dcb8b156ed47b31865db2c9f837)) +* **tests:** add test for transformationPosition as path in signed URL generation ([2f37641](https://github.com/imagekit-developer/imagekit-nodejs/commit/2f37641776756aaae377c802f46e2ee6349127eb)) +* **tests:** add tests for transformation handling with absolute URLs and non-default endpoints ([188eeee](https://github.com/imagekit-developer/imagekit-nodejs/commit/188eeee3b77d7e3a89e8c5abad4e0fef0ca9107f)) +* update README to enhance SDK description and usage examples ([e1e5abf](https://github.com/imagekit-developer/imagekit-nodejs/commit/e1e5abf48ffd9845a359082aa0b8ef10adeb7b7f)) +* **webhooks:** use toBase64 for webhook key in verification ([433eb44](https://github.com/imagekit-developer/imagekit-nodejs/commit/433eb44c54f3211d1b80aa97935a705ce7968a8a)) +* **webhooks:** use toBase64 for webhook key in verification ([3d0571d](https://github.com/imagekit-developer/imagekit-nodejs/commit/3d0571dbe9fa9cdd04f23a2f6d56a49005596649)) -# Before (Pre-version 5.3.0) -result.forEach((item) => { - console.log(item); -}); -# After (Version 5.3.0 and above) -result.forEach((item) => { - if (item.type === "folder") { - console.log(item) // item is of type FolderObject - } else { - console.log(item) // item is of type FileObject - } -}); -``` +### Bug Fixes +* 24 ([5610765](https://github.com/imagekit-developer/imagekit-nodejs/commit/56107650b674572551057c3788e0857ece5e5e7c)) +* add repository details for package ([b9e4231](https://github.com/imagekit-developer/imagekit-nodejs/commit/b9e423142ab909ce9f0034e73d26c6d350ade4da)) +* added folder object in ListFileResponse ([#106](https://github.com/imagekit-developer/imagekit-nodejs/issues/106)) ([bfcfbb9](https://github.com/imagekit-developer/imagekit-nodejs/commit/bfcfbb9ed2c82aea7284ed6841d3a92afd2fb0da)) +* coerce nullable values to undefined ([66e3b81](https://github.com/imagekit-developer/imagekit-nodejs/commit/66e3b81cc8d6a1a123c0622c08801ecbdeef4f9f)) +* correct SDK description in package.json ([f5d2713](https://github.com/imagekit-developer/imagekit-nodejs/commit/f5d2713a54e1f0a1fc3c1c36546a7ad5d3f6783f)) +* **docs:** add missing commas in URL generation examples for clarity ([21caa93](https://github.com/imagekit-developer/imagekit-nodejs/commit/21caa9336a890568790d5b2bb49c274ed2434c4e)) +* **package:** removed unnecessary types and install-types package ([a254d4b](https://github.com/imagekit-developer/imagekit-nodejs/commit/a254d4b5f5cef576fba3499d77cafc13b521f7bb)) +* update privateAPIKey to privateKey in code and tests ([2f93b89](https://github.com/imagekit-developer/imagekit-nodejs/commit/2f93b891233782e8f6af350905a979f683173458)) +* updated signed url generations for urls with symbols and unicode characters ([#102](https://github.com/imagekit-developer/imagekit-nodejs/issues/102)) ([5e264de](https://github.com/imagekit-developer/imagekit-nodejs/commit/5e264dedf6b5fbc9e98b66e715726eb7b2b1cfba)) +* **webhooks:** revert toBase64 conversion for webhook key ([13c716e](https://github.com/imagekit-developer/imagekit-nodejs/commit/13c716e35e73c8ad79157b818ac93b45365be8f3)) -### SDK Version 5.0.0 -#### Breaking changes +### Chores -**1. Overlay syntax update** -* In version 5.0.0, we've removed the old overlay syntax parameters for transformations, such as `oi`, `ot`, `obg`, and [more](https://docs.imagekit.io/features/image-transformations/overlay). These parameters are deprecated and will start returning errors when used in URLs. Please migrate to the new layers syntax that supports overlay nesting, provides better positional control, and allows more transformations at the layer level. You can start with [examples](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#examples) to learn quickly. -* You can migrate to the new layers syntax using the `raw` transformation parameter. +* bumped package version to 6.0.0 ([85c7ef3](https://github.com/imagekit-developer/imagekit-nodejs/commit/85c7ef34f4c624d3b292ffe4115718607ec1e98d)) +* ci build action ([06a9882](https://github.com/imagekit-developer/imagekit-nodejs/commit/06a988278c597a54f8d7e7b5c23d62cfae4079b7)) +* do not install brew dependencies in ./scripts/bootstrap by default ([69968b1](https://github.com/imagekit-developer/imagekit-nodejs/commit/69968b160ccf3e4dbd68a6356714d75dd0d63acb)) +* **esm:** Improved Support for ES Modules ([5a4127f](https://github.com/imagekit-developer/imagekit-nodejs/commit/5a4127fb4c3b6c7d007043cf51d3c0687ef68ac0)) +* lint and format fix ([788885c](https://github.com/imagekit-developer/imagekit-nodejs/commit/788885c3cc5e8834105ec2b0b8ed28ac747b0b1a)) +* sync repo ([3b95a96](https://github.com/imagekit-developer/imagekit-nodejs/commit/3b95a962395d62aee0c8133efce3bc863a0332bf)) +* update SDK settings ([9ea85e3](https://github.com/imagekit-developer/imagekit-nodejs/commit/9ea85e33b6484aa6a62c178c51d9522756750297)) +* **workflow:** added node 16 and 18 to test suite ([ef277ca](https://github.com/imagekit-developer/imagekit-nodejs/commit/ef277ca3e3f7d3801d9ea7a54929a9cd47837134)) -**2. Remove Node.js 10.x support** -* In version 5.0.0, we've removed support for Node.js version 10.x. + +### Documentation + +* update to make it more readable ([ed5ff38](https://github.com/imagekit-developer/imagekit-nodejs/commit/ed5ff38d6d9576a70c8115d9ed1e54f537277d8a)) + + +### Refactors + +* enhance README for clarity and detail on SDK features ([569545c](https://github.com/imagekit-developer/imagekit-nodejs/commit/569545c17e7ccf80cffcbe1ef847a70e4f3d07d9)) +* **helper:** remove console error logging in Helper class ([cc1a4c0](https://github.com/imagekit-developer/imagekit-nodejs/commit/cc1a4c0d915a9dfc6b1156f578fb1e713f965c2e)) +* **tests:** remove redundant helper tests ([ef30e9c](https://github.com/imagekit-developer/imagekit-nodejs/commit/ef30e9c65b9259bbc5bef259a565789c1502dae8)) +* **tests:** remove unused imports from URL generation test files ([2e7211e](https://github.com/imagekit-developer/imagekit-nodejs/commit/2e7211e34f56a45e909db054a5dc739dc824d6e4)) +* **tests:** update URL generation test to include new aiEdit transformation parameter ([a18331d](https://github.com/imagekit-developer/imagekit-nodejs/commit/a18331d25a731109106a8e7c5c63a884e851d854)) +* **transformation-utils:** replace safeBtoa implementation with toBase64 utility; update overlay tests for consistency ([e4adc14](https://github.com/imagekit-developer/imagekit-nodejs/commit/e4adc14a0662f9782665bdff8865229819618995)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..b7920529 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +## Setting up the environment + +This repository uses [`yarn@v1`](https://classic.yarnpkg.com/lang/en/docs/install). +Other package managers may work but are not officially supported for development. + +To set up the repository, run: + +```sh +$ yarn +$ yarn build +``` + +This will install all the required dependencies and build output files to `dist/`. + +## Modifying/Adding code + +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/lib/` and `examples/` directories. + +## Adding and running examples + +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. + +```ts +// add an example to examples/.ts + +#!/usr/bin/env -S npm run tsn -T +… +``` + +```sh +$ chmod +x examples/.ts +# run the example against your api +$ yarn tsn -T examples/.ts +``` + +## Using the repository from source + +If you’d like to use the repository from source, you can either install from git or link to a cloned repository: + +To install via git: + +```sh +$ npm install git+ssh://git@github.com:imagekit-developer/imagekit-nodejs.git +``` + +Alternatively, to link a local copy of the repo: + +```sh +# Clone +$ git clone https://www.github.com/imagekit-developer/imagekit-nodejs +$ cd imagekit-nodejs + +# With yarn +$ yarn link +$ cd ../my-package +$ yarn link @imagekit/nodejs + +# With pnpm +$ pnpm link --global +$ cd ../my-package +$ pnpm link -—global @imagekit/nodejs +``` + +## Running tests + +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. + +```sh +$ npx prism mock path/to/your/openapi.yml +``` + +```sh +$ yarn run test +``` + +## Linting and formatting + +This repository uses [prettier](https://www.npmjs.com/package/prettier) and +[eslint](https://www.npmjs.com/package/eslint) to format the code in the repository. + +To lint: + +```sh +$ yarn lint +``` + +To format and fix all lint issues automatically: + +```sh +$ yarn fix +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e7a4d160 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Image Kit + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 37a4832f..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -Released under the MIT license. \ No newline at end of file diff --git a/README.md b/README.md index dbbf7a59..b40708e7 100644 --- a/README.md +++ b/README.md @@ -1,1293 +1,636 @@ -[ImageKit.io](https://imagekit.io) - # ImageKit.io Node.js SDK -[![Node CI](https://github.com/imagekit-developer/imagekit-nodejs/workflows/Node%20CI/badge.svg)](https://github.com/imagekit-developer/imagekit-nodejs/) -[![npm version](https://img.shields.io/npm/v/imagekit)](https://www.npmjs.com/package/imagekit) -[![codecov](https://codecov.io/gh/imagekit-developer/imagekit-nodejs/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-nodejs) -[![Try imagekit on RunKit](https://badge.runkitcdn.com/imagekit.svg)](https://npm.runkit.com/imagekit) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Twitter Follow](https://img.shields.io/twitter/follow/imagekitio?label=Follow&style=social)](https://twitter.com/ImagekitIo) - -Node.js SDK for [ImageKit](https://imagekit.io/) implements the new APIs and interface for different file operations. - -ImageKit is complete media storage, optimization, and transformation solution that comes with an [image and video CDN](https://imagekit.io/features/imagekit-infrastructure). It can be integrated with your existing infrastructure - storage like AWS S3, web servers, your CDN, and custom domain names, allowing you to deliver optimized images in minutes with minimal code changes. - -##### Table of contents -* [Changelog](#changelog) -* [Installation](#installation) -* [Initialization](#initialization) -* [URL generation](#url-generation) -* [File upload](#file-upload) -* [File management](#file-management) -* [Utility functions](#utility-functions) -* [Rate limits](#rate-limits) -* [Support](#support) -* [Links](#links) +[![NPM version]()](https://npmjs.org/package/@imagekit/nodejs) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@imagekit/nodejs) -## Installation +The ImageKit Node.js SDK is a comprehensive library designed to simplify the integration of ImageKit into your server-side applications. It provides powerful tools for working with the ImageKit REST API, including building and transforming URLs, generating signed URLs for secure content delivery, verifying webhooks, and handling file uploads. With robust TypeScript support, this SDK ensures excellent type safety and a seamless developer experience. -Use the following command to download this module. Use the optional `--save` parameter if you wish to save the dependency in your `package.json` file. +The full API of this library is documented in [api.md](api.md). All request parameters and response types are fully typed and importable, offering unparalleled TypeScript support. This ensures that you can rely on accurate type definitions and enjoy a smooth development workflow with modern editors. -``` -npm install imagekit --save -# or -pnpm install imagekit --save -# or -bun install imagekit // if you are using [Bun](https://bun.sh/) compiler -# or -yarn add imagekit -``` +For additional details, refer to the [ImageKit REST API documentation](https://imagekit.io/docs/api-reference). -## Initialization +If you are looking to integrate file uploads in browsers, use one of our [frontend SDKs](https://imagekit.io/docs/quick-start-guides#front-end). -```js -import ImageKit from "imagekit"; - -// or - -var ImageKit = require("imagekit"); +## Installation -var imagekit = new ImageKit({ - publicKey : "your_public_api_key", - privateKey : "your_private_api_key", - urlEndpoint : "https://ik.imagekit.io/your_imagekit_id/" -}); +```sh +npm install git+ssh://git@github.com:imagekit-developer/imagekit-nodejs.git ``` -## Usage -You can use this Node.js SDK for three different methods - URL generation, file upload, and media management operations. The usage of the SDK has been explained below. - -* `URL Generation` -* `File Upload` -* `File Management` - -## URL Generation +> [!NOTE] +> Once this package is [published to npm](https://www.stainless.com/docs/guides/publish), this will become: `npm install @imagekit/nodejs` -**1. Using image path and image hostname or endpoint** +## Usage -This method allows you to create an URL to access a file using the relative file path and the ImageKit URL endpoint (`urlEndpoint`). The file can be an image, video or any other static file supported by ImageKit. +The full API of this library can be found in [api.md](api.md). + ```js -// For URL Generation, works for both images and videos -var imageURL = imagekit.url({ - path : "/default-image.jpg", - urlEndpoint : "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation : [{ - "height" : "300", - "width" : "400" - }] -}); -``` - -This results in a URL like - -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/tr:h-300,w-400/default-image.jpg -``` - -**2. Using full image URL** - -This method allows you to add transformation parameters to an absolute URL. For example, if you have configured a custom CNAME and have absolute asset URLs in your database or CMS, you will often need this. +import ImageKit from '@imagekit/nodejs'; - -```js -var imageURL = imagekit.url({ - src : "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation : [{ - "height" : "300", - "width" : "400" - }] +const client = new ImageKit({ + privateKey: process.env['IMAGEKIT_PRIVATE_KEY'], // This is the default and can be omitted }); -``` - -This results in a URL like - -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400 -``` - - -The `.url()` method accepts the following parameters - -| Option | Description | -| :----------------| :----------------------------- | -| urlEndpoint | Optional. The base URL to be appended before the path of the image. If not specified, the URL Endpoint specified at the time of SDK initialization is used. For example, https://ik.imagekit.io/your_imagekit_id/endpoint/ | -| path | Conditional. This is the path at which the image exists. For example, `/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | -| src | Conditional. This is the complete URL of an image already mapped to ImageKit. For example, `https://ik.imagekit.io/your_imagekit_id/endpoint/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | -| transformation | Optional. An array of objects specifying the transformation to be applied in the URL. The transformation name and the value should be specified as a key-value pair in the object. Different steps of a [chained transformation](https://docs.imagekit.io/features/image-transformations/chained-transformations) can be specified as different objects of the array. The complete list of supported transformations in the SDK and some examples of using them are given later. If you use a transformation name that is not specified in the SDK, it gets applied as it is in the URL. | -| transformationPosition | Optional. The default value is `path` that places the transformation string as a path parameter in the URL. It can also be specified as `query`, which adds the transformation string as the URL's query parameter `tr`. If you use the `src` parameter to create the URL, then the transformation string is always added as a query parameter. | -| queryParameters | Optional. These are the other query parameters that you want to add to the final URL. These can be any query parameters and not necessarily related to ImageKit. Especially useful if you want to add some versioning parameter to your URLs. | -| signed | Optional. Boolean. Default is `false`. If set to `true`, the SDK generates a signed image URL adding the image signature to the image URL. If you create a URL using the `src` parameter instead of `path`, then do correct `urlEndpoint` for this to work. Otherwise returned URL will have the wrong signature | -| expireSeconds | Optional. Integer. Meant to be used along with the `signed` parameter to specify the time in seconds from now when the URL should expire. If specified, the URL contains the expiry timestamp in the URL, and the image signature is modified accordingly. | - -#### Examples of generating URLs -**1. Chained Transformations as a query parameter** -```js -var imageURL = imagekit.url({ - path : "/default-image.jpg", - urlEndpoint : "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation : [{ - "height" : "300", - "width" : "400" - }, { - "rotation" : 90 - }], - transformationPosition : "query" +const response = await client.files.upload({ + file: fs.createReadStream('path/to/file'), + fileName: 'file-name.jpg', }); -``` -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400%3Art-90 -``` -**2. Sharpening and contrast transforms and a progressive JPG image** - -There are some transforms like [Sharpening](https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation) that can be added to the URL with or without any other value. To use such transforms without specifying a value, specify the value as "-" in the transformation object. Otherwise, specify the value that you want to be added to this transformation. - -```js -var imageURL = imagekit.url({ - src : "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation : [{ - "format" : "jpg", - "progressive" : "true", - "effectSharpen" : "-", - "effectContrast" : "1" - }] -}); -``` -``` -//Note that because the `src` parameter was used, the transformation string gets added as a query parameter `tr` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=f-jpg%2Cpr-true%2Ce-sharpen%2Ce-contrast-1 -``` - -**3. Signed URL that expires in 300 seconds with the default URL endpoint and other query parameters** -```js -var imageURL = imagekit.url({ - path : "/default-image.jpg", - queryParameters : { - "v" : "123" - }, - transformation : [{ - "height" : "300", - "width" : "400" - }], - signed : true, - expireSeconds : 300 -}); -``` +console.log(response); ``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400/default-image.jpg?v=123&ik-t=1567358667&ik-s=f2c7cdacbe7707b71a83d49cf1c6110e3d701054 -``` - -**4. Adding overlays** -ImageKit.io enables you to apply overlays to [images](https://docs.imagekit.io/features/image-transformations/overlay-using-layers) and [videos](https://docs.imagekit.io/features/video-transformation/overlay) using the raw parameter with the concept of [layers](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#layers). The raw parameter facilitates incorporating transformations directly in the URL. A layer is a distinct type of transformation that allows you to define an asset to serve as an overlay, along with its positioning and additional transformations. +### Request & Response types -**Text as overlays** +This library includes TypeScript definitions for all request params and response fields. You may import and use them like so: -You can add any text string over a base video or image using a text layer (l-text). + +```ts +import ImageKit from '@imagekit/nodejs'; -For example: - -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", - transformation: [{ - "width": 400, - "height": 300 - "raw": "l-text,i-Imagekit,fs-50,l-end" - }] +const client = new ImageKit({ + privateKey: process.env['IMAGEKIT_PRIVATE_KEY'], // This is the default and can be omitted }); -``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-text,i-Imagekit,fs-50,l-end/default-image.jpg -``` - -**Image as overlays** - -You can add an image over a base video or image using an image layer (l-image). -For example: - -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", - transformation: [{ - "width": 400, - "height": 300 - "raw": "l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end" - }] -}); -``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end/default-image.jpg +const params: ImageKit.FileUploadParams = { + file: fs.createReadStream('path/to/file'), + fileName: 'file-name.jpg', +}; +const response: ImageKit.FileUploadResponse = await client.files.upload(params); ``` -**Solid color blocks as overlays** +Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors. -You can add solid color blocks over a base video or image using an image layer (l-image). +## File uploads -For example: +Request parameters that correspond to file uploads can be passed in many different forms: -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/img/sample-video.mp4", - transformation: [{ - "width": 400, - "height": 300 - "raw": "l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end" - }] -}); -``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end/img/sample-video.mp4 -``` +- `File` (or an object with the same structure) +- a `fetch` `Response` (or an object with the same structure) +- an `fs.ReadStream` +- the return value of our `toFile` helper -**5. Arithmetic expressions in transformations** +```ts +import fs from 'fs'; +import ImageKit, { toFile } from '@imagekit/nodejs'; -ImageKit allows use of [arithmetic expressions](https://docs.imagekit.io/features/arithmetic-expressions-in-transformations) in certain dimension and position-related parameters, making media transformations more flexible and dynamic. +const client = new ImageKit(); -For example: +// If you have access to Node `fs` we recommend using `fs.createReadStream()`: +await client.files.upload({ file: fs.createReadStream('/path/to/file'), fileName: 'fileName' }); -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", - transformation: [{ - "width": "iw_div_4", - "height": "ih_div_2", - "border": "cw_mul_0.05_yellow" - }] -}); -``` - -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:w-iw_div_4,h-ih_div_2,b-cw_mul_0.05_yellow/default-image.jpg -``` +// Or if you have the web `File` API you can pass a `File` instance: +await client.files.upload({ file: new File(['my bytes'], 'file'), fileName: 'fileName' }); +// You can also pass a `fetch` `Response`: +await client.files.upload({ file: await fetch('https://somesite/file'), fileName: 'fileName' }); -#### List of supported transformations - -See the complete list of transformations supported in ImageKit [here](https://docs.imagekit.io/features/image-transformations). The SDK gives a name to each transformation parameter e.g. `height` for `h` and `width` for `w` parameter. It makes your code more readable. If the property does not match any of the following supported options, it is added as it is. - -If you want to generate transformations in your application and add them to the URL as it is, use the `raw` parameter. - - -| Supported Transformation Name | Translates to parameter | -|-------------------------------|-------------------------| -| height | h | -| width | w | -| aspectRatio | ar | -| quality | q | -| crop | c | -| cropMode | cm | -| x | x | -| y | y | -| focus | fo | -| format | f | -| radius | r | -| background | bg | -| border | b | -| rotation | rt | -| blur | bl | -| named | n | -| progressive | pr | -| lossless | lo | -| trim | t | -| metadata | md | -| colorProfile | cp | -| defaultImage | di | -| dpr | dpr | -| effectSharpen | e-sharpen | -| effectUSM | e-usm | -| effectContrast | e-contrast | -| effectGray | e-grayscale | -| effectShadow | e-shadow | -| effectGradient | e-gradient | -| original | orig | -| raw | `replaced by the parameter value` | - - - -## File Upload - -The SDK provides a simple interface using the `.upload()` method to upload files to the ImageKit Media Library. It accepts all the parameters supported by the [ImageKit Upload API](https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload). - -The `upload()` method requires at least the `file` and the `fileName` parameter to upload a file and returns a callback with the `error` and `result` as arguments. You can pass other parameters supported by the ImageKit upload API using the same parameter name as specified in the upload API documentation. For example, to set tags for a file at the upload time, use the `tags` parameter as defined in the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload). - -Sample usage -```js -// Using Callback Function - -imagekit.upload({ - file : , //required - fileName : "my_file_name.jpg", //required - extensions: [ - { - name: "google-auto-tagging", - maxTags: 5, - minConfidence: 95 - } - ], - transformation: { - pre: 'l-text,i-Imagekit,fs-50,l-end', - post: [ - { - type: 'transformation', - value: 'w-100' - } - ] - }, - checks: {`"file.size" < "1mb"`}, // To run server side checks before uploading files. Notice the quotes around file.size and 1mb. - isPublished: true -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - -// Using Promises - -imagekit.upload({ - file : , //required - fileName : "my_file_name.jpg", //required - extensions: [ - { - name: "google-auto-tagging", - maxTags: 5, - minConfidence: 95 - } - ], - transformation: { - pre: 'l-text,i-Imagekit,fs-50,l-end', - post: [ - { - type: 'transformation', - value: 'w-100' - } - ] - }, - checks={`"file.size" < "1mb"`} -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +// Finally, if none of the above are convenient, you can use our `toFile` helper: +await client.files.upload({ file: await toFile(Buffer.from('my bytes'), 'file'), fileName: 'fileName' }); +await client.files.upload({ file: await toFile(new Uint8Array([0, 1, 2]), 'file'), fileName: 'fileName' }); ``` -If the upload succeeds, `error` will be `null,` and the `result` will be the same as what is received from ImageKit's servers. -If the upload fails, the `error` will be the same as what is received from ImageKit's servers, and the `result` will be null. - +## URL generation +The ImageKit SDK provides a powerful `helper.buildSrc()` method for generating optimized image and video URLs with transformations. Here are examples ranging from simple URLs to complex transformations with overlays and signed URLs. -## File Management +### Basic URL generation -The SDK provides a simple interface for all the [media APIs mentioned here](https://docs.imagekit.io/api-reference/media-api) to manage your files. You can use a callback function with all API interfaces. The first argument of the callback function is the error, and the second is the result of the API call. The error will be `null` if the API succeeds. +Generate a simple URL without any transformations: -**List & Search Files** +```ts +import ImageKit from '@imagekit/nodejs'; -Accepts an object specifying the parameters used to list and search files. All parameters specified in the [documentation here](https://docs.imagekit.io/api-reference/media-api/list-and-search-files) can be passed as-is with the correct values to get the results. - -```js -// Using Callback Function - -imagekit.listFiles({ - skip : 10, - limit : 10 -}, function(error, result) { - if(error) console.log(error); - else console.log(result); +const client = new ImageKit({ + privateKey: process.env['IMAGEKIT_PRIVATE_KEY'] }); - -// Using Promises - -imagekit.listFiles({ - skip : 10, - limit : 10 -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +// Basic URL without transformations +const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/path/to/image.jpg', }); +// Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg ``` -**Get File Details** - -Accepts the file ID and fetches the details as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-details). - -```js -// Using Callback Function - -imagekit.getFileDetails("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +### URL generation with transformations +Apply common transformations like resizing, cropping, and format conversion: -// Using Promises - -imagekit.getFileDetails("file_id") -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +// URL with basic transformations +const transformedUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/path/to/image.jpg', + transformation: [ + { + width: 400, + height: 300, + crop: 'maintain_ratio', + quality: 80, + format: 'webp', + }, + ], }); +// Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300,c-maintain_ratio,q-80,f-webp ``` -**Get File Versions** - -Accepts the file ID and fetches all the versions of that file as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-versions). - -```js -// Using Callback Function - -imagekit.getFileVersions("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - +### URL generation with image overlay -// Using Promises +Add image overlays to your base image: -imagekit.getFileVersions("file_id") -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +// URL with image overlay +const imageOverlayUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/path/to/base-image.jpg', + transformation: [ + { + width: 500, + height: 400, + overlay: { + type: 'image', + input: '/path/to/overlay-logo.png', + position: { + x: 10, + y: 10, + }, + transformation: [ + { + width: 100, + height: 50, + }, + ], + }, + }, + ], }); +// Result: URL with image overlay positioned at x:10, y:10 ``` -**Get File version details** - -Accepts the file ID & version ID and returns the details of that specific version as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/get-file-version-details). - -```js -// Using Callback Function - -imagekit.getFileVersionDetails({ - fileId: "file_id", - versionId: "version_id" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +### URL generation with text overlay +Add customized text overlays: -// Using Promises - -imagekit.getFileVersionDetails({ - fileId: "file_id", - versionId: "version_id" -}) -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +// URL with text overlay +const textOverlayUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/path/to/base-image.jpg', + transformation: [ + { + width: 600, + height: 400, + overlay: { + type: 'text', + text: 'Sample Text Overlay', + position: { + x: 50, + y: 50, + focus: 'center', + }, + transformation: [ + { + fontSize: 40, + fontFamily: 'Arial', + fontColor: 'FFFFFF', + typography: 'b', // bold + }, + ], + }, + }, + ], }); +// Result: URL with bold white Arial text overlay at center position ``` -**Update File Details** - -Update parameters associated with the file as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/update-file-details). The first argument to the `updateFileDetails` method is the file ID, and the second argument is an object with the parameters to be updated. - -Note: If `publish` is included in the update options, no other parameters are allowed. If any are present, an error will be returned: `Your request cannot contain any other parameters when publish is present`. +### URL generation with multiple overlays -```js -// Using Callback Function - -imagekit.updateFileDetails("file_id", { - tags : ['image_tag'], - customCoordinates : "10,10,100,100", - extensions: [ - { - name: "google-auto-tagging", - maxTags: 5, - minConfidence: 95 - } - ] -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +Combine multiple overlays for complex compositions: - -// Using Promises - -imagekit.updateFileDetails("file_id", { - publish: { - isPublished: true, - includeFileVersions: true - } -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +// URL with multiple overlays (text + image) +const multipleOverlaysUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/path/to/base-image.jpg', + transformation: [ + { + width: 800, + height: 600, + overlay: { + type: 'text', + text: 'Header Text', + position: { x: 20, y: 20 }, + transformation: [{ fontSize: 30, fontColor: '000000' }], + }, + }, + { + overlay: { + type: 'image', + input: '/watermark.png', + position: { focus: 'bottom_right' }, + transformation: [{ width: 100, opacity: 70 }], + }, + }, + ], }); +// Result: URL with text overlay at top-left and semi-transparent watermark at bottom-right ``` -**Bulk Add tags** +### Signed URLs for secure delivery -Add tags to multiple files in a single request as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/add-tags-bulk). The method accepts an array of fileIDs of the files and an array of tags that have to be added to those files. - -```js -// Using Callback Function +Generate signed URLs that expire after a specified time for secure content delivery: -imagekit.bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"], function(error, result) { - if(error) console.log(error); - else console.log(result); +```ts +// Generate a signed URL that expires in 1 hour (3600 seconds) +const signedUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/private/secure-image.jpg', + transformation: [ + { + width: 400, + height: 300, + quality: 90, + }, + ], + signed: true, + expiresIn: 3600, // URL expires in 1 hour }); +// Result: URL with signature parameters (?ik-t=timestamp&ik-s=signature) -// Using Promises - -imagekit.bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +// Generate a signed URL that doesn't expire +const permanentSignedUrl = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/your_imagekit_id', + src: '/private/secure-image.jpg', + signed: true, + // No expiresIn means the URL won't expire }); +// Result: URL with signature parameter (?ik-s=signature) ``` -**Bulk Remove tags** +## Authentication parameters for client-side uploads -Remove tags from multiple files in a single request as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/remove-tags-bulk). The method accepts an array of fileIDs of the files and an array of tags that have to be removed from those files. +Generate authentication parameters for secure client-side file uploads: -```js -// Using Callback Function +```ts +// Generate authentication parameters for client-side uploads +const authParams = client.helper.getAuthenticationParameters(); +console.log(authParams); +// Result: { token: 'uuid-token', expire: timestamp, signature: 'hmac-signature' } -imagekit.bulkRemoveTags(["file_id_1", "file_id_2"], ["tags"], function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - -// Using Promises - -imagekit.bulkRemoveTags(["file_id_1", "file_id_2"], ["tags"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +// Generate with custom token and expiry +const customAuthParams = client.helper.getAuthenticationParameters('my-custom-token', 1800); +console.log(customAuthParams); +// Result: { token: 'my-custom-token', expire: 1800, signature: 'hmac-signature' } ``` -**Bulk Remove AI Tags** +These authentication parameters can be used in client-side upload forms to securely upload files without exposing your private API key. -Remove AI tags from multiple files in a single request as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/remove-aitags-bulk). The method accepts an array of fileIDs of the files and an array of tags that have to be removed from those files. +## Webhook verification -```js -// Using Callback Function +The ImageKit SDK provides utilities to verify webhook signatures for secure event handling. This ensures that webhook requests are actually coming from ImageKit and haven't been tampered with. -imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"], function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +### Verifying webhook signatures -// Using Promises +```ts +import ImageKit from '@imagekit/nodejs'; -imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + privateKey: process.env['IMAGEKIT_PRIVATE_KEY'], + webhookSecret: process.env['IMAGEKIT_WEBHOOK_SECRET'], // Required for webhook verification }); -``` - -**Delete File** - -Delete a file as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/delete-file). The method accepts the file ID of the file that has to be deleted. -```js -// Using Callback Function - -imagekit.deleteFile("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - - -// Using Promises - -imagekit.deleteFile("file_id").then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +try { + // Verify and unwrap webhook payload + const event = client.webhooks.unwrap( + webhookBody, // Raw webhook payload (string) + { + headers: webhookHeaders, // Request headers containing signature + } + ); + + console.log('Webhook signature is valid'); + console.log('Event type:', event.type); + console.log('Event data:', event.data); + // Process the webhook event +} catch (error) { + console.log('Invalid webhook signature or malformed payload'); + // Reject the request +} ``` -**Delete File Version** - -Delete any non-current version of a file as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/delete-file-version). +For detailed information about webhook setup, signature verification, and handling different webhook events, refer to the [ImageKit webhook documentation](https://imagekit.io/docs/webhooks#verify-webhook-signature). -```js -// Using Callback Function - -imagekit.deleteFileVersion({ - fileId: "file_id", - versionId: "version_id", -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +## Handling errors +When the library is unable to connect to the API, +or if the API returns a non-success status code (i.e., 4xx or 5xx response), +a subclass of `APIError` will be thrown: -// Using Promises - -imagekit.deleteFile({ - fileId: "file_id", - versionId: "version_id", -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); + +```ts +const response = await client.files + .upload({ file: fs.createReadStream('path/to/file'), fileName: 'file-name.jpg' }) + .catch(async (err) => { + if (err instanceof ImageKit.APIError) { + console.log(err.status); // 400 + console.log(err.name); // BadRequestError + console.log(err.headers); // {server: 'nginx', ...} + } else { + throw err; + } + }); ``` -**Bulk Delete Files** - -Delete multiple files as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/delete-files-bulk). The method accepts an array of file IDs of the files that have to be deleted. +Error codes are as follows: -```js -// Using Callback Function - -imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"], function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | +### Retries -// Using Promises +Certain errors will be automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors will all be retried by default. -imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` - -**Copy File** - -This will copy a file from one location to another as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/copy-file). +You can use the `maxRetries` option to configure or disable this: + ```js -// Using Callback Function - -imagekit.copyFile({ - sourceFilePath: "/path/to/file.jpg", - destinationPath: "/folder/to/copy/into/" - includeFileVersions: false // optional -}, function(error, result) { - if(error) console.log(error); - else console.log(result); +// Configure the default for all requests: +const client = new ImageKit({ + maxRetries: 0, // default is 2 }); -// Using Promises - -imagekit.copyFile({ - sourceFilePath: "/path/to/file.jpg", - destinationPath: "/folder/to/copy/into/", - includeFileVersions: false // optional -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +// Or, configure per-request: +await client.files.upload({ file: fs.createReadStream('path/to/file'), fileName: 'file-name.jpg' }, { + maxRetries: 5, }); ``` -**Move File** +### Timeouts -This will move a file from one location to another as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/move-file). +Requests time out after 1 minute by default. You can configure this with a `timeout` option: -```js -// Using Callback Function - -imagekit.moveFile({ - sourceFilePath: "/path/to/file.jpg", - destinationPath: "/folder/to/copy/into/", -}, function(error, result) { - if(error) console.log(error); - else console.log(result); + +```ts +// Configure the default for all requests: +const client = new ImageKit({ + timeout: 20 * 1000, // 20 seconds (default is 1 minute) }); -// Using Promises - -imagekit.moveFile({ - sourceFilePath: "/path/to/file.jpg", - destinationPath: "/folder/to/copy/into/", -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +// Override per-request: +await client.files.upload({ file: fs.createReadStream('path/to/file'), fileName: 'file-name.jpg' }, { + timeout: 5 * 1000, }); ``` -**Rename File** +On timeout, an `APIConnectionTimeoutError` is thrown. -Rename the file as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/rename-file). +Note that requests which time out will be [retried twice by default](#retries). -```js -// Using Callback Function - -imagekit.renameFile({ - filePath: "/path/to/old-file-name.jpg", - newFileName: "new-file-name.jpg", - purgeCache: false // optional -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +## Advanced Usage -// Using Promises +### Accessing raw Response data (e.g., headers) -imagekit.renameFile({ - filePath: "/path/to/old-file-name.jpg", - newFileName: "new-file-name.jpg", - purgeCache: false // optional -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` +The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return. +This method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic. -**Restore File Version** +You can also use the `.withResponse()` method to get the raw `Response` along with the parsed data. +Unlike `.asResponse()` this method consumes the body, returning once it is parsed. -Restore the file version as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/restore-file-version). + +```ts +const client = new ImageKit(); -```js -// Using Callback Function - -imagekit.restoreFileVersion({ - fileId: "file_id", - versionId: "version_id" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +const response = await client.files + .upload({ file: fs.createReadStream('path/to/file'), fileName: 'file-name.jpg' }) + .asResponse(); +console.log(response.headers.get('X-My-Header')); +console.log(response.statusText); // access the underlying Response object -// Using Promises - -imagekit.restoreFileVersion({ - fileId: "file_id", - versionId: "version_id" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +const { data: response, response: raw } = await client.files + .upload({ file: fs.createReadStream('path/to/file'), fileName: 'file-name.jpg' }) + .withResponse(); +console.log(raw.headers.get('X-My-Header')); +console.log(response.videoCodec); ``` -**Create Folder** +### Logging -This will create a new folder as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/create-folder). +> [!IMPORTANT] +> All log messages are intended for debugging only. The format and content of log messages +> may change between releases. -```js -// Using Callback Function - -imagekit.createFolder({ - folderName: "new_folder", - parentFolderPath: "source/folder/path" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - -// Using Promises +#### Log levels -imagekit.createFolder({ - folderName: "new_folder", - parentFolderPath: "source/folder/path" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` +The log level can be configured in two ways: -**Delete Folder** +1. Via the `IMAGE_KIT_LOG` environment variable +2. Using the `logLevel` client option (overrides the environment variable if set) -This will delete the specified Folder and all nested files & folders as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/delete-folder). +```ts +import ImageKit from '@imagekit/nodejs'; -```js -// Using Callback Function - -imagekit.deleteFolder("folderPath", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - -// Using Promises - -imagekit.deleteFolder("folderPath").then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + logLevel: 'debug', // Show all log messages }); ``` -**Copy Folder** +Available log levels, from most to least verbose: -This will copy one Folder into another as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/copy-folder). +- `'debug'` - Show debug messages, info, warnings, and errors +- `'info'` - Show info messages, warnings, and errors +- `'warn'` - Show warnings and errors (default) +- `'error'` - Show only errors +- `'off'` - Disable all logging -```js -// Using Callback Function - -imagekit.copyFolder({ - sourceFolderPath: "/folder/to/copy", - destinationPath: "/folder/to/copy/into/", - includeFileVersions: false // optional -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +At the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies. +Some authentication-related headers are redacted, but sensitive data in request and response bodies +may still be visible. -// Using Promises +#### Custom logger -imagekit.copyFolder({ - sourceFolderPath: "/folder/to/copy", - destinationPath: "/folder/to/copy/into/", - includeFileVersions: false // optional -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` +By default, this library logs to `globalThis.console`. You can also provide a custom logger. +Most logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue. -**Move Folder** +When providing a custom logger, the `logLevel` option still controls which messages are emitted, messages +below the configured level will not be sent to your logger. -This will move one Folder into another as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/move-folder). +```ts +import ImageKit from '@imagekit/nodejs'; +import pino from 'pino'; -```js -// Using Callback Function - -imagekit.moveFolder({ - sourceFolderPath: "/folder/to/move", - destinationPath: "/folder/to/move/into/" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +const logger = pino(); -// Using Promises - -imagekit.moveFolder({ - sourceFolderPath: "/folder/to/move", - destinationPath: "/folder/to/move/into/" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + logger: logger.child({ name: 'ImageKit' }), + logLevel: 'debug', // Send all messages to pino, allowing it to filter }); ``` -**Get bulk job status** - -This allows us to get a bulk operation status e.g. copy or move Folder as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/copy-move-folder-status). This method accepts `jobId` that is returned by copy and move folder operations. +### Making custom/undocumented requests -```js -// Using Callback Function +This library is typed for convenient access to the documented API. If you need to access undocumented +endpoints, params, or response properties, the library can still be used. -imagekit.getBulkJobStatus("jobId", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +#### Undocumented endpoints -// Using Promises +To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs. +Options on the client, such as retries, will be respected when making these requests. -imagekit.getBulkJobStatus("jobId").then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +await client.post('/some/path', { + body: { some_prop: 'foo' }, + query: { some_query_arg: 'bar' }, }); ``` -**Purge Cache** +#### Undocumented request params -Programmatically issue a clear cache request as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/purge-cache). Accepts the full URL of the file for which the cache has to be cleared. +To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented +parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you +send will be sent as-is. -```js -// Using Callback Function - -imagekit.purgeCache("full_url", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - - -// Using Promises - -imagekit.purgeCache("full_url").then(response => { - console.log(response); -}).catch(error => { - console.log(error); +```ts +client.files.upload({ + // ... + // @ts-expect-error baz is not yet public + baz: 'undocumented option', }); ``` -**Purge Cache Status** +For requests with the `GET` verb, any extra params will be in the query, all other requests will send the +extra param in the body. -Get the purge cache request status using the request ID returned when a purge cache request gets submitted as per the [API documentation here](https://docs.imagekit.io/api-reference/media-api/purge-cache-status) +If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request +options. -```js -// Using Callback Function +#### Undocumented response properties -imagekit.getPurgeCacheStatus("cache_request_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +To access undocumented response properties, you may access the response object with `// @ts-expect-error` on +the response object, or cast the response object to the requisite type. Like the request params, we do not +validate or strip extra properties from the response from the API. +### Customizing the fetch client -// Using Promises +By default, this library expects a global `fetch` function is defined. -imagekit.getPurgeCacheStatus("cache_request_id").then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` +If you want to use a different `fetch` function, you can either polyfill the global: -**Get File Metadata** +```ts +import fetch from 'my-fetch'; -Accepts the file ID and fetches the metadata as per the [API documentation here](https://docs.imagekit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files). - -```js -// Using Callback Function -imagekit.getFileMetadata("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - - -// Using Promises -imagekit.getFileMetadata("file_id") -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +globalThis.fetch = fetch; ``` -You can also pass the remote URL of the image to get metadata. +Or pass it to the client: -```js -// Using Callback Function -imagekit.getFileMetadata("https://ik.imagekit.io/your_imagekit_id/sample.jpg", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +```ts +import ImageKit from '@imagekit/nodejs'; +import fetch from 'my-fetch'; - -// Using Promises -imagekit.getFileMetadata("https://ik.imagekit.io/your_imagekit_id/sample.jpg") -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +const client = new ImageKit({ fetch }); ``` -**Create a custom metadata field** +### Fetch options -Create a new custom metadata field as per the [API documentation here](https://docs.imagekit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field) +If you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.) -```js -// Using Callback Function +```ts +import ImageKit from '@imagekit/nodejs'; -imagekit.createCustomMetadataField( - { - name: "price", - label: "price", - schema: { - type: "Number", - minValue: 1000, - maxValue: 3000 - } - }, - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); - - -// Using Promises - -imagekit.createCustomMetadataField( - { - name: "price", - label: "price", - schema: { - type: "Number", - minValue: 1000, - maxValue: 3000 - } - } -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + fetchOptions: { + // `RequestInit` options + }, }); ``` -**Get all custom metadata fields** +#### Configuring proxies -Get the list of all custom metadata fields as per the [API documentation here](https://docs.imagekit.io/api-reference/custom-metadata-fields-api/get-custom-metadata-field) - -```js -// Using Callback Function - -imagekit.getCustomMetadataFields( - { - includeDeleted: false // optional - }, - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); +To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy +options to requests: + **Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)] -// Using Promises +```ts +import ImageKit from '@imagekit/nodejs'; +import * as undici from 'undici'; -imagekit.getCustomMetadataFields( - { - includeDeleted: false // optional - } -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const proxyAgent = new undici.ProxyAgent('http://localhost:8888'); +const client = new ImageKit({ + fetchOptions: { + dispatcher: proxyAgent, + }, }); ``` -**Update a custom metadata field** + **Bun** [[docs](https://bun.sh/guides/http/proxy)] -Update a custom metadata field as per the [API documentation here](https://docs.imagekit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field) +```ts +import ImageKit from '@imagekit/nodejs'; -```js -// Using Callback Function - -imagekit.updateCustomMetadataField( - "field_id", - { - schema: { - minValue: 500, - maxValue: 2500 - } - }, - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); - - -// Using Promises - -imagekit.updateCustomMetadataField( - "field_id", - { - schema: { - minValue: 500, - maxValue: 2500 - } - }, -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + fetchOptions: { + proxy: 'http://localhost:8888', + }, }); ``` -**Delete a custom metadata field** + **Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)] -delete a custom metadata field as per the [API documentation here](https://docs.imagekit.io/api-reference/custom-metadata-fields-api/delete-custom-metadata-field) - -```js -// Using Callback Function - -imagekit.deleteCustomMetadataField( - "field_id", - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); +```ts +import ImageKit from 'npm:@imagekit/nodejs'; - -// Using Promises - -imagekit.deleteCustomMetadataField( - "field_id" -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } }); +const client = new ImageKit({ + fetchOptions: { + client: httpClient, + }, }); ``` -## Utility functions - -We have included the following commonly used utility functions in this package. - -### Authentication parameter generation - -If you want to implement client-side file upload, you will need a token, expiry timestamp, and a valid signature for that upload. The SDK provides a simple method you can use in your backend code to generate these authentication parameters. - -*Note: The Private API Key should never be exposed in any client-side code. You must always generate these authentication parameters on the server-side* - -```js -var authenticationParameters = imagekit.getAuthenticationParameters(token, expire); -``` - -Returns -```js -{ - token : "unique_token", - expire : "valid_expiry_timestamp", - signature : "generated_signature" -} -``` - -Both the `token` and `expire` parameters are optional. If not specified, the SDK uses the [uuid](https://www.npmjs.com/package/uuid) package to generate a random token and generate a valid expiry timestamp internally. `token` and `expire` are always returned in the response, no matter if they are provided as an input to this method or not. - -### Distance calculation between two pHash values - -Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on an image's contents. For example, [ImageKit.io metadata API](https://docs.imagekit.io/api-reference/metadata-api) returns the pHash value of an image in the response. You can use this value to [find a duplicate (or similar) image](https://docs.imagekit.io/api-reference/metadata-api#using-phash-to-find-similar-or-duplicate-images) by calculating the distance between the two images' pHash value. - -This SDK exposes the `pHashDistance` function to calculate the distance between two pHash values. It accepts two pHash hexadecimal strings and returns a numeric value indicative of the level of difference between the two images. +## Frequently Asked Questions -```js -const calculateDistance = () => { - // asynchronously fetch metadata of two uploaded image files - // ... - // Extract pHash strings from both: say 'firstHash' and 'secondHash' - // ... - // Calculate the distance between them: - const distance = imagekit.pHashDistance(firstHash, secondHash); - return distance; -} -``` -#### Distance calculation examples +## Semantic versioning -```js -imagekit.pHashDistance('f06830ca9f1e3e90', 'f06830ca9f1e3e90'); -// output: 0 (same image) +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: -imagekit.pHashDistance('2d5ad3936d2e015b', '2d6ed293db36a4fb'); -// output: 17 (similar images) +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. -imagekit.pHashDistance('a4a65595ac94518b', '7838873e791f8400'); -// output: 37 (dissimilar images) -``` -## Access request-id, other response headers and HTTP status code -You can access `$ResponseMetadata` on success or error object to access the HTTP status code and response headers. +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -```javascript -// Success -var response = await imagekit.getPurgeCacheStatus(requestId); -console.log(response.$ResponseMetadata.statusCode); // 200 +We are keen for your feedback; please open an [issue](https://www.github.com/imagekit-developer/imagekit-nodejs/issues) with questions, bugs, or suggestions. -// {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} -console.log(response.$ResponseMetadata.headers); +## Requirements -// Error -try { - await imagekit.getPurgeCacheStatus(requestId); -} catch (ex) { - console.log(response.$ResponseMetadata.statusCode); // 404 - - // {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} - console.log(response.$ResponseMetadata.headers); -} -``` +TypeScript >= 4.9 is supported. -## Rate limits -Except for upload API, all [ImageKit APIs are rate limited](https://docs.imagekit.io/api-reference/api-introduction/rate-limits) to protect the infrastructure from excessive requests rates and to keep ImageKit.io fast and stable for everyone. - -When you exceed the rate limits for an endpoint, you will receive a `429` status code. The Node.js library reads the [rate limiting response headers](https://docs.imagekit.io/api-reference/api-introduction/rate-limits#response-headers-to-understand-rate-limits) provided in the API response and adds these in the error argument of the callback or `.catch` when using promises. Please sleep/pause for the number of milliseconds specified by the value of the `X-RateLimit-Reset` property before making additional requests to that endpoint. - -| Property | Description | -|----------|-------------| -| `X-RateLimit-Limit` | The maximum number of requests that can be made to this endpoint in the interval specified by the `X-RateLimit-Interval` response header. | -| `X-RateLimit-Reset` | The amount of time in milliseconds before you can make another request to this endpoint. Pause/sleep your workflow for this duration. | -| `X-RateLimit-Interval` | The duration of interval in milliseconds for which this rate limit was exceeded. | - -## Verify webhook events - -ImageKit sends `x-ik-signature` in the webhook request header, which can be used to verify the authenticity of the webhook request. - -Verifying webhook signature is easy with imagekit SDK. All you need is the value of the `x-ik-signature` header, request body, and [webhook secret](https://imagekit.io/dashboard/developer/webhooks) from the ImageKit dashboard. - -Here is an example using the express.js server. - -```js -const express = require('express'); -const Imagekit = require('imagekit'); - -// Webhook configs -const WEBHOOK_SECRET = 'whsec_...'; // Copy from Imagekit dashboard -const WEBHOOK_EXPIRY_DURATION = 300 * 1000; // 300 seconds for example - -const imagekit = new Imagekit({ - publicKey: 'public_...', - urlEndpoint: 'https://ik.imagekit.io/imagekit_id', - privateKey: 'private_...', -}) - -const app = express(); - -app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { - const signature = req.headers["x-ik-signature"]; - const requestBody = req.body; - let webhookResult; - try { - webhookResult = imagekit.verifyWebhookEvent(requestBody, signature, WEBHOOK_SECRET); - } catch (e) { - // `verifyWebhookEvent` method will throw an error if signature is invalid - console.log(e); - res.status(400).send(`Webhook Error`); - } - - const { timestamp, event } = webhookResult; - - // Check if webhook has expired - if (timestamp + WEBHOOK_EXPIRY_DURATION < Date.now()) { - res.status(400).send(`Webhook Error`); - } - - // Handle webhook - switch (event.type) { - case 'video.transformation.accepted': - // It is triggered when a new video transformation request is accepted for processing. You can use this for debugging purposes. - break; - case 'video.transformation.ready': - // It is triggered when a video encoding is finished, and the transformed resource is ready to be served. You should listen to this webhook and update any flag in your database or CMS against that particular asset so your application can start showing it to users. - break; - case 'video.transformation.error': - // It is triggered if an error occurs during encoding. Listen to this webhook to log the reason. You should check your origin and URL-endpoint settings if the reason is related to download failure. If the reason seems like an error on the ImageKit side, then raise a support ticket at support@imagekit.io. - break; - default: - // ... handle other event types - console.log(`Unhandled event type ${event.type}`); - } - - // Return a response to acknowledge receipt of the event - res.send(); -}) - -app.listen(3000, () => { - console.log(`Example app listening on port 3000`) -}) -``` +The following runtimes are supported: -## Support +- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more) +- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions. +- Deno v1.28.0 or higher. +- Bun 1.0 or later. +- Cloudflare Workers. +- Vercel Edge Runtime. +- Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time). +- Nitro v2.6 or greater. -For any feedback or to report any issues or general implementation support, please reach out to [support@imagekit.io](mailto:support@imagekit.io) +Note that React Native is not supported at this time. -## Links -* [Documentation](https://docs.imagekit.io) -* [Main website](https://imagekit.io) +If you are interested in other runtime environments, please open or upvote an issue on GitHub. -## License +## Contributing -Released under the MIT license. +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..8e64327a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Image Kit, please follow the respective company's security reporting guidelines. + +### Image Kit Terms and Policies + +Please contact developer@imagekit.io for any questions or concerns regarding the security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/api.md b/api.md new file mode 100644 index 00000000..86719ecf --- /dev/null +++ b/api.md @@ -0,0 +1,227 @@ +# Shared + +Types: + +- BaseOverlay +- Extensions +- ImageOverlay +- Overlay +- OverlayPosition +- OverlayTiming +- SolidColorOverlay +- SolidColorOverlayTransformation +- SrcOptions +- StreamingResolution +- SubtitleOverlay +- SubtitleOverlayTransformation +- TextOverlay +- TextOverlayTransformation +- Transformation +- TransformationPosition +- VideoOverlay + +# CustomMetadataFields + +Types: + +- CustomMetadataField +- CustomMetadataFieldListResponse +- CustomMetadataFieldDeleteResponse + +Methods: + +- client.customMetadataFields.create({ ...params }) -> CustomMetadataField +- client.customMetadataFields.update(id, { ...params }) -> CustomMetadataField +- client.customMetadataFields.list({ ...params }) -> CustomMetadataFieldListResponse +- client.customMetadataFields.delete(id) -> CustomMetadataFieldDeleteResponse + +# Files + +Types: + +- File +- Folder +- Metadata +- UpdateFileRequest +- FileUpdateResponse +- FileCopyResponse +- FileMoveResponse +- FileRenameResponse +- FileUploadResponse + +Methods: + +- client.files.update(fileID, { ...params }) -> FileUpdateResponse +- client.files.delete(fileID) -> void +- client.files.copy({ ...params }) -> FileCopyResponse +- client.files.get(fileID) -> File +- client.files.move({ ...params }) -> FileMoveResponse +- client.files.rename({ ...params }) -> FileRenameResponse +- client.files.upload({ ...params }) -> FileUploadResponse + +## Bulk + +Types: + +- BulkDeleteResponse +- BulkAddTagsResponse +- BulkRemoveAITagsResponse +- BulkRemoveTagsResponse + +Methods: + +- client.files.bulk.delete({ ...params }) -> BulkDeleteResponse +- client.files.bulk.addTags({ ...params }) -> BulkAddTagsResponse +- client.files.bulk.removeAITags({ ...params }) -> BulkRemoveAITagsResponse +- client.files.bulk.removeTags({ ...params }) -> BulkRemoveTagsResponse + +## Versions + +Types: + +- VersionListResponse +- VersionDeleteResponse + +Methods: + +- client.files.versions.list(fileID) -> VersionListResponse +- client.files.versions.delete(versionID, { ...params }) -> VersionDeleteResponse +- client.files.versions.get(versionID, { ...params }) -> File +- client.files.versions.restore(versionID, { ...params }) -> File + +## Metadata + +Methods: + +- client.files.metadata.get(fileID) -> Metadata +- client.files.metadata.getFromURL({ ...params }) -> Metadata + +# Assets + +Types: + +- AssetListResponse + +Methods: + +- client.assets.list({ ...params }) -> AssetListResponse + +# Cache + +## Invalidation + +Types: + +- InvalidationCreateResponse +- InvalidationGetResponse + +Methods: + +- client.cache.invalidation.create({ ...params }) -> InvalidationCreateResponse +- client.cache.invalidation.get(requestID) -> InvalidationGetResponse + +# Folders + +Types: + +- FolderCreateResponse +- FolderDeleteResponse +- FolderCopyResponse +- FolderMoveResponse +- FolderRenameResponse + +Methods: + +- client.folders.create({ ...params }) -> FolderCreateResponse +- client.folders.delete({ ...params }) -> FolderDeleteResponse +- client.folders.copy({ ...params }) -> FolderCopyResponse +- client.folders.move({ ...params }) -> FolderMoveResponse +- client.folders.rename({ ...params }) -> FolderRenameResponse + +## Job + +Types: + +- JobGetResponse + +Methods: + +- client.folders.job.get(jobID) -> JobGetResponse + +# Accounts + +## Usage + +Types: + +- UsageGetResponse + +Methods: + +- client.accounts.usage.get({ ...params }) -> UsageGetResponse + +## Origins + +Types: + +- OriginRequest +- OriginResponse +- OriginListResponse + +Methods: + +- client.accounts.origins.create({ ...params }) -> OriginResponse +- client.accounts.origins.update(id, { ...params }) -> OriginResponse +- client.accounts.origins.list() -> OriginListResponse +- client.accounts.origins.delete(id) -> void +- client.accounts.origins.get(id) -> OriginResponse + +## URLEndpoints + +Types: + +- URLEndpointRequest +- URLEndpointResponse +- URLEndpointListResponse + +Methods: + +- client.accounts.urlEndpoints.create({ ...params }) -> URLEndpointResponse +- client.accounts.urlEndpoints.update(id, { ...params }) -> URLEndpointResponse +- client.accounts.urlEndpoints.list() -> URLEndpointListResponse +- client.accounts.urlEndpoints.delete(id) -> void +- client.accounts.urlEndpoints.get(id) -> URLEndpointResponse + +# Beta + +## V2 + +### Files + +Types: + +- FileUploadResponse + +Methods: + +- client.beta.v2.files.upload({ ...params }) -> FileUploadResponse + +# Webhooks + +Types: + +- BaseWebhookEvent +- UploadPostTransformErrorEvent +- UploadPostTransformSuccessEvent +- UploadPreTransformErrorEvent +- UploadPreTransformSuccessEvent +- VideoTransformationAcceptedEvent +- VideoTransformationErrorEvent +- VideoTransformationReadyEvent +- UnsafeUnwrapWebhookEvent +- UnwrapWebhookEvent + +Methods: + +- client.webhooks.unsafeUnwrap(body) -> void +- client.webhooks.unwrap(body) -> void diff --git a/babel-register.js b/babel-register.js deleted file mode 100644 index a24dfd20..00000000 --- a/babel-register.js +++ /dev/null @@ -1,3 +0,0 @@ -const register = require("@babel/register").default; - -register({ extensions: [".ts", ".tsx", ".js", ".jsx"] }); diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 00000000..6b43775a --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +errors=() + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" + diff --git a/bin/publish-npm b/bin/publish-npm new file mode 100644 index 00000000..45e8aa80 --- /dev/null +++ b/bin/publish-npm @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -eux + +npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN" + +yarn build +cd dist + +# Get package name and version from package.json +PACKAGE_NAME="$(jq -r -e '.name' ./package.json)" +VERSION="$(jq -r -e '.version' ./package.json)" + +# Get latest version from npm +# +# If the package doesn't exist, npm will return: +# { +# "error": { +# "code": "E404", +# "summary": "Unpublished on 2025-06-05T09:54:53.528Z", +# "detail": "'the_package' is not in this registry..." +# } +# } +NPM_INFO="$(npm view "$PACKAGE_NAME" version --json 2>/dev/null || true)" + +# Check if we got an E404 error +if echo "$NPM_INFO" | jq -e '.error.code == "E404"' > /dev/null 2>&1; then + # Package doesn't exist yet, no last version + LAST_VERSION="" +elif echo "$NPM_INFO" | jq -e '.error' > /dev/null 2>&1; then + # Report other errors + echo "ERROR: npm returned unexpected data:" + echo "$NPM_INFO" + exit 1 +else + # Success - get the version + LAST_VERSION=$(echo "$NPM_INFO" | jq -r '.') # strip quotes +fi + +# Check if current version is pre-release (e.g. alpha / beta / rc) +CURRENT_IS_PRERELEASE=false +if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then + CURRENT_IS_PRERELEASE=true + CURRENT_TAG="${BASH_REMATCH[1]}" +fi + +# Check if last version is a stable release +LAST_IS_STABLE_RELEASE=true +if [[ -z "$LAST_VERSION" || "$LAST_VERSION" =~ -([a-zA-Z]+) ]]; then + LAST_IS_STABLE_RELEASE=false +fi + +# Use a corresponding alpha/beta tag if there already is a stable release and we're publishing a prerelease. +if $CURRENT_IS_PRERELEASE && $LAST_IS_STABLE_RELEASE; then + TAG="$CURRENT_TAG" +else + TAG="latest" +fi + +# Publish with the appropriate tag +yarn publish --tag "$TAG" diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..c1a01a62 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,42 @@ +// @ts-check +import tseslint from 'typescript-eslint'; +import unusedImports from 'eslint-plugin-unused-imports'; +import prettier from 'eslint-plugin-prettier'; + +export default tseslint.config( + { + languageOptions: { + parser: tseslint.parser, + parserOptions: { sourceType: 'module' }, + }, + files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.js', '**/*.mjs', '**/*.cjs'], + ignores: ['dist/'], + plugins: { + '@typescript-eslint': tseslint.plugin, + 'unused-imports': unusedImports, + prettier, + }, + rules: { + 'no-unused-vars': 'off', + 'prettier/prettier': 'error', + 'unused-imports/no-unused-imports': 'error', + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + regex: '^@imagekit/nodejs(/.*)?', + message: 'Use a relative import, not a package import.', + }, + ], + }, + ], + }, + }, + { + files: ['tests/**', 'examples/**', 'packages/**'], + rules: { + 'no-restricted-imports': 'off', + }, + }, +); diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 00000000..0651c89c --- /dev/null +++ b/examples/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store example files demonstrating usage of this SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. diff --git a/index.ts b/index.ts deleted file mode 100644 index 59d984fc..00000000 --- a/index.ts +++ /dev/null @@ -1,681 +0,0 @@ -/* - Helper Modules -*/ -import _ from "lodash"; -import errorMessages from "./libs/constants/errorMessages"; -import { - BulkDeleteFilesError, - BulkDeleteFilesResponse, - CopyFolderError, - CopyFolderResponse, - FileDetailsOptions, - FileObject, - FolderObject, - FileMetadataResponse, - ImageKitOptions, - ListFileOptions, - ListFileResponse, - MoveFolderError, - MoveFolderResponse, - PurgeCacheResponse, - PurgeCacheStatusResponse, - UploadOptions, - UploadResponse, - UrlOptions, - CopyFileOptions, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - FileVersionDetailsOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - CreateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - RenameFileOptions, - RenameFileResponse, -} from "./libs/interfaces"; -import { IKCallback } from "./libs/interfaces/IKCallback"; -import manage from "./libs/manage"; -import signature from "./libs/signature"; -import upload from "./libs/upload"; -import { verify as verifyWebhookEvent } from "./utils/webhook-signature"; -import customMetadataField from "./libs/manage/custom-metadata-field"; -/* - Implementations -*/ -import url from "./libs/url"; -/* - Utils -*/ -import pHashUtils from "./utils/phash"; -import transformationUtils from "./utils/transformation"; -import IKResponse from "./libs/interfaces/IKResponse"; - -const promisify = function (thisContext: ImageKit, fn: Function) { - return function (...args: any[]): Promise | void { - if (args.length === fn.length && typeof args[args.length - 1] !== "undefined") { - if (typeof args[args.length - 1] !== "function") { - throw new Error("Callback must be a function."); - } - fn.call(thisContext, ...args); - } else { - return new Promise((resolve, reject) => { - const callback = function (err: Error, ...results: any[]) { - if (err) { - return reject(err); - } else { - resolve(results.length > 1 ? results : results[0]); - } - }; - args.pop() - args.push(callback); - fn.call(thisContext, ...args); - }); - } - }; -}; -class ImageKit { - options: ImageKitOptions = { - uploadEndpoint: "https://upload.imagekit.io/api/v1/files/upload", - publicKey: "", - privateKey: "", - urlEndpoint: "", - transformationPosition: transformationUtils.getDefault(), - }; - - constructor(opts: ImageKitOptions = {} as ImageKitOptions) { - this.options = _.extend(this.options, opts); - if (!this.options.publicKey) { - throw new Error(errorMessages.MANDATORY_PUBLIC_KEY_MISSING.message); - } - if (!this.options.privateKey) { - throw new Error(errorMessages.MANDATORY_PRIVATE_KEY_MISSING.message); - } - if (!this.options.urlEndpoint) { - throw new Error(errorMessages.MANDATORY_URL_ENDPOINT_KEY_MISSING.message); - } - } - - /** - * This method allows you to create an URL to access a file using the relative or absolute path and the ImageKit URL endpoint (urlEndpoint). The file can be an image, video or any other static file supported by ImageKit. - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#url-generation} - * @see {@link https://docs.imagekit.io/integration/url-endpoints} - * - * @param urlOptions - */ - - url(urlOptions: UrlOptions): string { - return url(urlOptions, this.options); - } - - /** - * You can upload file to ImageKit.io media library from your server-side using private API key authentication. - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload} - * - * @param uploadOptions - */ - upload(uploadOptions: UploadOptions): Promise>; - upload(uploadOptions: UploadOptions, callback: IKCallback>): void; - upload( - uploadOptions: UploadOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, upload)(uploadOptions, this.options, callback); - } - - /** - * This API can list all the uploaded files in your ImageKit.io media library. - * For searching and filtering, you can use query parameters as described in docs. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - * - * @param listFilesOptions - */ - listFiles(listOptions: ListFileOptions): Promise>; - listFiles(listOptions: ListFileOptions, callback: IKCallback>): void; - listFiles( - listOptions: ListFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.listFiles)(listOptions, this.options, callback); - } - - /** - * Get the file details such as tags, customCoordinates, and isPrivate properties using get file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-details} - * - * @param fileId - */ - getFileDetails(fileId: string): Promise>; - getFileDetails(fileId: string, callback: IKCallback>): void; - getFileDetails( - fileId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileDetails)(fileId, this.options, callback); - } - - /** - * Get all versions of an assset. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-versions} - * - * @param fileId - */ - getFileVersions(fileId: string): Promise>; - getFileVersions(fileId: string, callback: IKCallback>): void; - getFileVersions( - fileId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileVersions)(fileId, this.options, callback); - } - - /** - * Get file details of a specific version. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-version-details} - * - * @param fileVersionDetailsOptions - */ - getFileVersionDetails(fileVersionDetailsOptions: FileVersionDetailsOptions): Promise>; - getFileVersionDetails( - fileVersionDetailsOptions: FileVersionDetailsOptions, - callback: IKCallback>, - ): void; - getFileVersionDetails( - fileVersionDetailsOptions: FileVersionDetailsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileVersionDetails)( - fileVersionDetailsOptions, - this.options, - callback, - ); - } - - /** - * Get image exif, pHash and other metadata for uploaded files in ImageKit.io media library using this API. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files} - * - * @param fileIdOrURL The unique fileId of the uploaded file or absolute URL. - */ - getFileMetadata(fileIdOrURL: string): Promise>; - getFileMetadata(fileIdOrURL: string, callback: IKCallback>): void; - getFileMetadata( - fileIdOrURL: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileMetadata)( - fileIdOrURL, - this.options, - callback, - ); - } - - /** - * Update file details such as tags and customCoordinates attribute using update file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/update-file-details} - * - * @param fileId The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - * @param updateData - */ - updateFileDetails(fileId: string, updateData: FileDetailsOptions): Promise>; - updateFileDetails(fileId: string, updateData: FileDetailsOptions, callback: IKCallback>): void; - updateFileDetails( - fileId: string, - updateData: FileDetailsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.updateFileDetails)( - fileId, - updateData, - this.options, - callback, - ); - } - - /** - * Add tags to multiple files in a single request. The method accepts an array of fileIDs of the files and an array of tags that have to be added to those files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/add-tags-bulk} - * - * @param fileIds - * @param tags - */ - bulkAddTags(fileIds: string[], tags: string[]): Promise>; - bulkAddTags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkAddTags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkAddTags)(fileIds, tags, this.options, callback); - } - - /** - * Remove tags to multiple files in a single request. The method accepts an array of fileIDs of the files and an array of tags that have to be removed to those files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/remove-tags-bulk} - * - * @param fileIds - * @param tags - */ - bulkRemoveTags(fileIds: string[], tags: string[]): Promise>; - bulkRemoveTags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkRemoveTags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkRemoveTags)(fileIds, tags, this.options, callback); - } - - /** - * Remove AITags from multiple files in a single request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/remove-aitags-bulk} - * - * @param fileIds - * @param tags - */ - bulkRemoveAITags(fileIds: string[], tags: string[]): Promise>; - bulkRemoveAITags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkRemoveAITags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkRemoveAITags)(fileIds, tags, this.options, callback); - } - - /** - * You can programmatically delete uploaded files in media library using delete file API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-file} - * - * @param fileId The unique fileId of the uploaded file. fileId is returned in list files API and upload API - */ - deleteFile(fileId: string): Promise>; - deleteFile(fileId: string, callback: IKCallback>): void; - deleteFile(fileId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.deleteFile)(fileId, this.options, callback); - } - - /** - * Delete any non-current version of a file. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-file-version} - * - * @param deleteFileVersionOptions - */ - deleteFileVersion(deleteFileVersionOptions: DeleteFileVersionOptions): Promise>; - deleteFileVersion(deleteFileVersionOptions: DeleteFileVersionOptions, callback: IKCallback>): void; - deleteFileVersion( - deleteFileVersionOptions: DeleteFileVersionOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.deleteFileVersion)( - deleteFileVersionOptions, - this.options, - callback, - ); - } - - /** - * Restore file version to a different version of a file. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/restore-file-version} - * - * @param restoreFileVersionOptions - */ - restoreFileVersion(restoreFileVersionOptions: RestoreFileVersionOptions): Promise>; - restoreFileVersion( - restoreFileVersionOptions: RestoreFileVersionOptions, - callback: IKCallback>, - ): void; - restoreFileVersion( - restoreFileVersionOptions: RestoreFileVersionOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.restoreFileVersion)( - restoreFileVersionOptions, - this.options, - callback, - ); - } - - /** - * This will purge CDN and ImageKit.io internal cache. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache} - * - * @param url The exact URL of the file to be purged. For example - https://ik.imageki.io/your_imagekit_id/rest-of-the-file-path.jpg - */ - purgeCache(url: string): Promise>; - purgeCache(url: string, callback: IKCallback>): void; - purgeCache( - url: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.purgeCache)(url, this.options, callback); - } - - /** - * Get the status of submitted purge request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache-status} - * - * @param requestId The requestId returned in response of purge cache API. - */ - getPurgeCacheStatus(requestId: string, callback: IKCallback>): void; - getPurgeCacheStatus(requestId: string): Promise>; - getPurgeCacheStatus( - requestId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getPurgeCacheStatus)( - requestId, - this.options, - callback, - ); - } - - /** - * Delete multiple files. The method accepts an array of file IDs of the files that have to be deleted. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-files-bulk} - * - * @param fileIdArray The requestId returned in response of purge cache API. - */ - bulkDeleteFiles( - fileIdArray: string[], - callback?: IKCallback, IKResponse>, - ): void | Promise>; - bulkDeleteFiles( - fileIdArray: string[], - callback?: IKCallback, IKResponse>, - ): void | Promise> { - return promisify>(this, manage.bulkDeleteFiles)( - fileIdArray, - this.options, - callback, - ); - } - - /** - * This will copy a file from one location to another. This method accepts the source file's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-file} - * - * @param copyFileOptions - */ - copyFile(copyFileOptions: CopyFileOptions): Promise>; - copyFile(copyFileOptions: CopyFileOptions, callback: IKCallback>): void; - copyFile( - copyFileOptions: CopyFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.copyFile)(copyFileOptions, this.options, callback); - } - - /** - * This will move a file from one location to another. This method accepts the source file's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-file} - * - * @param moveFileOptions - */ - moveFile(moveFileOptions: MoveFileOptions): Promise>; - moveFile(moveFileOptions: MoveFileOptions, callback: IKCallback>): void; - moveFile( - moveFileOptions: MoveFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.moveFile)(moveFileOptions, this.options, callback); - } - - /** - * You can programmatically rename an already existing file in the media library using rename file API. This operation would rename all file versions of the file. Note: The old URLs will stop working. The file/file version URLs cached on CDN will continue to work unless a purge is requested. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - * - * @param renameFileOptions - */ - renameFile(renameFileOptions: RenameFileOptions): Promise>; - renameFile(renameFileOptions: RenameFileOptions, callback: IKCallback>): void; - renameFile( - renameFileOptions: RenameFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.renameFile)( - renameFileOptions, - this.options, - callback, - ); - } - - /** - * This will create a new folder. This method accepts folder name and parent folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/create-folder} - * - * @param createFolderOptions - */ - createFolder(createFolderOptions: CreateFolderOptions): Promise>; - createFolder(createFolderOptions: CreateFolderOptions, callback: IKCallback>): void; - createFolder( - createFolderOptions: CreateFolderOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.createFolder)(createFolderOptions, this.options, callback); - } - - /** - * This will delete the specified folder and all nested files & folders. This method accepts the full path of the folder that is to be deleted. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-folder} - * - * @param foldePath - */ - deleteFolder(folderPath: string): Promise>; - deleteFolder(folderPath: string, callback: IKCallback>): void; - deleteFolder(folderPath: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.deleteFolder)(folderPath, this.options, callback); - } - - /** - * This will copy a folder from one location to another. This method accepts the source folder's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * @param copyFolderOptions - */ - copyFolder(copyFolderOptions: CopyFolderOptions): Promise>; - copyFolder( - copyFolderOptions: CopyFolderOptions, - callback: IKCallback, IKResponse>, - ): void; - copyFolder( - copyFolderOptions: CopyFolderOptions, - callback?: IKCallback, IKResponse>, - ): void | Promise> { - return promisify>(this, manage.copyFolder)( - copyFolderOptions, - this.options, - callback, - ); - } - - /** - * This will move a folder from one location to another. This method accepts the source folder's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * @param moveFolderOptions - */ - moveFolder(moveFolderOptions: MoveFolderOptions): Promise>; - moveFolder( - moveFolderOptions: MoveFolderOptions, - callback: IKCallback, IKResponse>, - ): void; - moveFolder( - moveFolderOptions: MoveFolderOptions, - callback?: IKCallback, MoveFolderError>, - ): void | Promise> { - return promisify>(this, manage.moveFolder)( - moveFolderOptions, - this.options, - callback, - ); - } - - /** - * In case you are looking to implement client-side file upload, you are going to need a token, expiry timestamp, and a valid signature for that upload. The SDK provides a simple method that you can use in your code to generate these authentication parameters for you. - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#authentication-parameter-generation} - * - * @param token - * @param expire - */ - getAuthenticationParameters(token?: string, expire?: number): { token: string; expire: number; signature: string } { - return signature.getAuthenticationParameters(token, expire, this.options); - } - - /** - * This allows us to get a bulk operation status e.g. copy or move folder. This method accepts jobId that is returned by copy and move folder operations. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * @param jobId - */ - getBulkJobStatus(jobId: string): Promise>; - getBulkJobStatus(jobId: string, callback: IKCallback>): Promise>; - getBulkJobStatus(jobId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.getBulkJobStatus)(jobId, this.options, callback); - } - - /** - * Create custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field} - * - * @param createCustomMetadataFieldOptions - */ - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - ): Promise>; - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - callback: IKCallback>, - ): Promise>; - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.create)( - createCustomMetadataFieldOptions, - this.options, - callback, - ); - } - - /** - *Get a list of all the custom metadata fields. - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/get-custom-metadata-field} - * - */ - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - ): Promise>; - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - callback: IKCallback>, - ): Promise>; - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.list)( - getCustomMetadataFieldsOptions, - this.options, - callback, - ); - } - - /** - * Update custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field} - * - * @param fieldId - * @param updateCustomMetadataFieldOptions - */ - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - ): Promise>; - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - callback: IKCallback>, - ): Promise>; - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.update)( - fieldId, - updateCustomMetadataFieldOptions, - this.options, - callback, - ); - } - - /** - * Delete a custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/delete-custom-metadata-field} - * - * @param fieldId - */ - deleteCustomMetadataField(fieldId: string): Promise>; - deleteCustomMetadataField(fieldId: string, callback: IKCallback>): void; - deleteCustomMetadataField(fieldId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, customMetadataField.deleteField)(fieldId, this.options, callback); - } - - /** - * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on an image's contents. ImageKit.io metadata API returns the pHash value of an image in the response. You can use this value to find a duplicate (or similar) image by calculating the distance between the two images' pHash value. - * - * This SDK exposes pHashDistance function to calculate the distance between two pHash values. It accepts two pHash hexadecimal strings and returns a numeric value indicative of the level of difference between the two images. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#perceptual-hash-phash} - * - * @param firstPHash - * @param secondPHash - */ - pHashDistance(firstPHash: string, secondPHash: string): number | Error { - return pHashUtils.pHashDistance(firstPHash, secondPHash); - } - - /** - * @param payload - Raw webhook request body (Encoded as UTF8 string or Buffer) - * @param signature - Webhook signature as UTF8 encoded strings (Stored in `x-ik-signature` header of the request) - * @param secret - Webhook secret as UTF8 encoded string [Copy from ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks) - * @returns \{ `timestamp`: Verified UNIX epoch timestamp if signature, `event`: Parsed webhook event payload \} - */ - verifyWebhookEvent = verifyWebhookEvent; -} - -export = ImageKit; diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..f7631d79 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,23 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + preset: 'ts-jest/presets/default-esm', + testEnvironment: 'node', + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }], + }, + moduleNameMapper: { + '^@imagekit/nodejs$': '/src/index.ts', + '^@imagekit/nodejs/(.*)$': '/src/$1', + }, + modulePathIgnorePatterns: [ + '/ecosystem-tests/', + '/dist/', + '/deno/', + '/deno_tests/', + '/packages/', + ], + testPathIgnorePatterns: ['scripts'], +}; + +export default config; diff --git a/libs/constants/errorMessages.ts b/libs/constants/errorMessages.ts deleted file mode 100644 index ba10e6cd..00000000 --- a/libs/constants/errorMessages.ts +++ /dev/null @@ -1,53 +0,0 @@ -export default { - "MANDATORY_INITIALIZATION_MISSING": { message: "Missing publicKey or privateKey or urlEndpoint during ImageKit initialization", help: "" }, - "MANDATORY_PUBLIC_KEY_MISSING": { message: "Missing publicKey during ImageKit initialization", help: "" }, - "MANDATORY_PRIVATE_KEY_MISSING": { message: "Missing privateKey during ImageKit initialization", help: "" }, - "MANDATORY_URL_ENDPOINT_KEY_MISSING": { message: "Missing urlEndpoint during ImageKit initialization", help: "" }, - "INVALID_TRANSFORMATION_POSITION": { message: "Invalid transformationPosition parameter", help: "" }, - "CACHE_PURGE_URL_MISSING": { message: "Missing URL parameter for this request", help: "" }, - "CACHE_PURGE_STATUS_ID_MISSING": { message: "Missing Request ID parameter for this request", help: "" }, - "FILE_ID_MISSING": { message: "Missing fileId parameter for this request", help: "" }, - "FILE_VERSION_ID_MISSING": { message: "Missing versionId parameter for this request", help: "" }, - "FILE_ID_OR_URL_MISSING": { message: "Pass either fileId or remote URL of the image as first parameter", help: "" }, - "INVALID_LIST_OPTIONS": { message: "Pass a valid JSON list options e.g. {skip: 10, limit: 100}.", help: "" }, - "UPDATE_DATA_MISSING": { message: "Missing file update data for this request", help: "" }, - "UPDATE_DATA_TAGS_INVALID": { message: "Invalid tags parameter for this request", help: "tags should be passed as null or an array like ['tag1', 'tag2']" }, - "UPDATE_DATA_COORDS_INVALID": { message: "Invalid customCoordinates parameter for this request", help: "customCoordinates should be passed as null or a string like 'x,y,width,height'" }, - "LIST_FILES_INPUT_MISSING": { message: "Missing options for list files", help: "If you do not want to pass any parameter for listing, pass an empty object" }, - "MISSING_UPLOAD_DATA": { message: "Missing data for upload", help: "" }, - "MISSING_UPLOAD_FILE_PARAMETER": { message: "Missing file parameter for upload", help: "" }, - "MISSING_UPLOAD_FILENAME_PARAMETER": { message: "Missing fileName parameter for upload", help: "" }, - "JOB_ID_MISSING": { message: "Missing jobId parameter", help: "" }, - "INVALID_DESTINATION_FOLDER_PATH": { message: "Invalid destinationPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_INCLUDE_VERSION": { message: "Invalid includeFileVersions value", help: "It should be a boolean" }, - "INVALID_SOURCE_FILE_PATH": { message: "Invalid sourceFilePath value", help: "It should be a string like /path/to/file.jpg'" }, - "INVALID_SOURCE_FOLDER_PATH": { message: "Invalid sourceFolderPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_FOLDER_NAME": { message: "Invalid folderName value", help: "" }, - "INVALID_PARENT_FOLDER_PATH": { message: "Invalid parentFolderPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_FOLDER_PATH": { message: "Invalid folderPath value", help: "It should be a string like '/path/to/folder'" }, - // pHash errors - "INVALID_PHASH_VALUE": { message: "Invalid pHash value", help: "Both pHash strings must be valid hexadecimal numbers" }, - "MISSING_PHASH_VALUE": { message: "Missing pHash value", help: "Please pass two pHash values" }, - "UNEQUAL_STRING_LENGTH": { message: "Unequal pHash string length", help: "For distance calucation, the two pHash strings must have equal length" }, - //bulk delete errors - "INVALID_FILEIDS_VALUE": { message: "Invalid value for fileIds", help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." }, - "BULK_ADD_TAGS_INVALID": { message: "Invalid value for tags", help: "tags should be a non empty array of string like ['tag1', 'tag2']." }, - "BULK_AI_TAGS_INVALID": { message: "Invalid value for AITags", help: "AITags should be a non empty array of string like ['tag1', 'tag2']." }, - "CMF_NAME_MISSING": { message: "Missing name parameter for this request", help: "" }, - "CMF_LABEL_MISSING": { message: "Missing label parameter for this request", help: "" }, - "CMF_SCHEMA_MISSING": { message: "Missing schema parameter for this request", help: "" }, - "CMF_SCHEMA_INVALID": { message: "Invalid value for schema", help: "schema should have a mandatory type field." }, - "CMF_LABEL_SCHEMA_MISSING": { message: "Both label and schema is missing", help: "" }, - "CMF_FIELD_ID_MISSING": { message: "Missing fieldId parameter for this request", help: "" }, - "INVALID_FILE_PATH": { message: "Invalid value for filePath", help: "Pass the full path of the file. For example - /path/to/file.jpg" }, - "INVALID_NEW_FILE_NAME": { message: "Invalid value for newFileName. It should be a string.", help: "" }, - "INVALID_PURGE_CACHE": { message: "Invalid value for purgeCache. It should be boolean.", help: "" }, - // Webhook signature - "VERIFY_WEBHOOK_EVENT_SIGNATURE_INCORRECT": { message: "Incorrect signature", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_SIGNATURE_MISSING": { message: "Signature missing", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_TIMESTAMP_MISSING": { message: "Timestamp missing", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_TIMESTAMP_INVALID": { message: "Timestamp invalid", help: "Please pass x-ik-signature header as utf8 string" }, - "INVALID_TRANSFORMATION": { message: "Invalid transformation parameter. Please include at least pre, post, or both.", help: ""}, - "INVALID_PRE_TRANSFORMATION": { message: "Invalid pre transformation parameter.", help: ""}, - "INVALID_POST_TRANSFORMATION": { message: "Invalid post transformation parameter.", help: ""}, -}; \ No newline at end of file diff --git a/libs/constants/supportedTransforms.ts b/libs/constants/supportedTransforms.ts deleted file mode 100644 index fb0e6498..00000000 --- a/libs/constants/supportedTransforms.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @see {@link https://docs.imagekit.io/features/image-transformations} - */ -const supportedTransforms = { - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#width-w} - */ - width: "w", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#height-h} - */ - height: "h", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#aspect-ratio-ar} - */ - aspectRatio: "ar", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#quality-q} - */ - quality: "q", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#crop-crop-modes-and-focus} - */ - crop: "c", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#crop-crop-modes-and-focus} - */ - cropMode: "cm", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#focus-fo} - */ - focus: "fo", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#examples-focus-using-cropped-image-coordinates} - */ - x: "x", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#examples-focus-using-cropped-image-coordinates} - */ - y: "y", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#format-f} - */ - format: "f", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#radius-r} - */ - radius: "r", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#background-color-bg} - */ - background: "bg", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#border-b} - */ - border: "b", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#rotate-rt} - */ - rotation: "rt", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#rotate-rt} - */ - rotate: "rt", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#blur-bl} - */ - blur: "bl", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#named-transformation-n} - */ - named: "n", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#progressive-image-pr} - */ - progressive: "pr", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#lossless-webp-and-png-lo} - */ - lossless: "lo", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#trim-edges-t} - */ - trim: "t", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#image-metadata-md} - */ - metadata: "md", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#color-profile-cp} - */ - colorProfile: "cp", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#default-image-di} - */ - defaultImage: "di", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#dpr-dpr} - */ - dpr: "dpr", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#sharpen-e-sharpen} - */ - effectSharpen: "e-sharpen", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#unsharp-mask-e-usm} - */ - effectUSM: "e-usm", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#contrast-stretch-e-contrast} - */ - effectContrast: "e-contrast", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#grayscale-e-grayscale} - */ - effectGray: "e-grayscale", - - /** - * @link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#shadow-e-shadow - */ - effectShadow: "e-shadow", - - /** - * @link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#gradient-e-gradient - */ - effectGradient: "e-gradient", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#original-image-orig} - */ - original: "orig", -}; - -export default supportedTransforms as { [key: string]: string }; -export type SupportedTransformsParam = keyof typeof supportedTransforms; diff --git a/libs/interfaces/BulkDeleteFiles.ts b/libs/interfaces/BulkDeleteFiles.ts deleted file mode 100644 index 9329db3f..00000000 --- a/libs/interfaces/BulkDeleteFiles.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Response when deleting multiple files from the media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-files-bulk} - */ -export interface BulkDeleteFilesResponse { - /** - * List of file ids of successfully deleted files - */ - successfullyDeletedFileIds: string[]; -} - -export interface BulkDeleteFilesError extends Error { - help: string; - missingFileIds: string[]; -} diff --git a/libs/interfaces/CopyFile.ts b/libs/interfaces/CopyFile.ts deleted file mode 100644 index eef7cab3..00000000 --- a/libs/interfaces/CopyFile.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface CopyFileOptions { - /** - * The full path of the file you want to copy. For example - /path/to/file.jpg - */ - sourceFilePath: string; - /** - * Full path to the folder you want to copy the above file into. For example - /folder/to/copy/into/ - */ - destinationPath: string; - /** - * Option to copy all versions of a file. By default, only the current version of the file is copied. When set to true, all versions of the file will be copied. - * Default value is false - */ - includeFileVersions?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/CopyFolder.ts b/libs/interfaces/CopyFolder.ts deleted file mode 100644 index c29d1a0f..00000000 --- a/libs/interfaces/CopyFolder.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Response when copying folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * On success, you will receive a jobId which can be used to get the copy operation's status. - */ -export interface CopyFolderResponse { - jobId: string; -} - -/** - * Error when copying folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * If no files or folders are found at the specified sourceFolderPath then a error is returned. - */ -export interface CopyFolderError extends Error { - help: string; - message: string; - reason: string; -} - -/** - * Copy folder API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - */ -export interface CopyFolderOptions { - /** - * The full path to the source folder you want to copy. For example - /path/of/source/folder. - */ - sourceFolderPath: string; - /** - * Full path to the destination folder where you want to copy the source folder into. For example - /path/of/destination/folder. - */ - destinationPath: string; - /** - * Option to copy all versions of files that are nested inside the selected folder. By default, only the current version of each file will be copied. When set to true, all versions of each file will be copied. - * Default value - false - */ - includeFileVersions?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/CreateFolder.ts b/libs/interfaces/CreateFolder.ts deleted file mode 100644 index 6f7c0025..00000000 --- a/libs/interfaces/CreateFolder.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CreateFolderOptions { - /** - * The folder will be created with this name. All characters except alphabets and numbers (inclusive of unicode letters, marks, and numerals in other languages) will be replaced by an underscore i.e. _. - */ - folderName: string; - /** - * The folder where the new folder should be created, for root use / else the path e.g. containing/folder/. - * Note: If any folder(s) is not present in the parentFolderPath parameter, it will be automatically created. For example, if you pass /product/images/summer, then product, images, and summer folders will be created if they don't already exist. - */ - parentFolderPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/CustomMetatadaField.ts b/libs/interfaces/CustomMetatadaField.ts deleted file mode 100644 index 7efcee06..00000000 --- a/libs/interfaces/CustomMetatadaField.ts +++ /dev/null @@ -1,123 +0,0 @@ -type RequiredSchema = { - isValueRequired: true; - defaultValue: T; -} | { - isValueRequired?: false; - defaultValue?: T; -}; - -type CustomMetadataTextField = RequiredSchema & { - type: "Text"; - minLength?: number; - maxLength?: number; -}; - -type CustomMetadataTextareaField = RequiredSchema & { - type: "Textarea"; - minLength?: number; - maxLength?: number; -}; - -type CustomMetadataNumberField = RequiredSchema & { - type: "Number"; - minValue?: string | number; - maxValue?: string | number; -}; - -type CustomMetadataDateField = RequiredSchema & { - type: "Date"; - minValue?: string | number; - maxValue?: string | number; -}; - -type CustomMetadataBooleanField = RequiredSchema & { - type: "Boolean"; -}; - -type CustomMetadataSingleSelectField = RequiredSchema> & { - type: "SingleSelect"; - selectOptions: Array; -}; - -type CustomMetadataMultiSelectField = RequiredSchema> & { - type: "MultiSelect"; - selectOptions: Array; -}; - -export type CustomMetadataFieldSchema = - | CustomMetadataTextField - | CustomMetadataTextareaField - | CustomMetadataNumberField - | CustomMetadataDateField - | CustomMetadataBooleanField - | CustomMetadataSingleSelectField - | CustomMetadataMultiSelectField; - -export type CustomMetadataFieldSchemaMinusType = - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit; - -/** - * Create a new custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field} - */ -export interface CreateCustomMetadataFieldOptions { - /** - * Name of the metadata field, unique across all (deleted or not deleted) custom metadata fields. - */ - name: string; - /** - * Label of the metadata field, unique across all non deleted custom metadata fields - */ - label: string; - /** - * An object that describes the rules for the custom metadata key. - */ - schema: CustomMetadataFieldSchema -} - -export interface CustomMetadataField { - id: string; - /** - * Name of the metadata field, unique across all (deleted or not deleted) custom metadata fields. - */ - name: string; - /** - * Label of the metadata field, unique across all non deleted custom metadata fields - */ - label: string; - /** - * An object that describes the rules for the custom metadata key. - */ - schema: CustomMetadataFieldSchema -} - -/** - * Update the label or schema of an existing custom metadata field. - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field} - */ -export interface UpdateCustomMetadataFieldOptions { - /** - * Label of the metadata field, unique across all non deleted custom metadata fields. This parameter is required if schema is not provided. - */ - label?: string; - /** - * An object that describes the rules for the custom metadata key. This parameter is required if label is not provided. - * Note: type cannot be updated and will be ignored if sent with the schema. The schema will be validated as per the existing type. - */ - schema?: CustomMetadataFieldSchemaMinusType -} - -export interface GetCustomMetadataFieldsOptions { - /** - * Set it to true if you want to receive deleted fields as well in the API response. - */ - includeDeleted?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/FileDetails.ts b/libs/interfaces/FileDetails.ts deleted file mode 100644 index 358ac538..00000000 --- a/libs/interfaces/FileDetails.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { FileType } from "./FileType"; - -export interface EmbeddedMetadataValues { - [key: string]: - | string - | number - | boolean - | Date - | Array -} - -export interface AITagItem { - name: string - confidence: number - source: 'google-auto-tagging' | 'aws-auto-tagging' -} - -export interface CMValues { - [key: string]: | string - | number - | boolean - | Array -} - -interface BgRemoval { - name: string - options: { - bg_color?: string - bg_image_url?: string - add_shadow: boolean - semitransparency: boolean - } -} - -interface AutoTag { - name: string - maxTags: number - minConfidence: number -} - -export type Extension = (BgRemoval | AutoTag)[]; - -/** - * Options when updating file details such as tags and customCoordinates attribute using update file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/update-file-details} - */ -export interface FileDetailsOptions { - /** - * Array of tags associated with the file. - */ - tags?: string[]; - /** - * Define an important area in the image. - * Example - 50,50,500,500 - */ - customCoordinates?: string; - /* - * Object with array of extensions to be processed on the image. - */ - extensions?: Extension; - /* - * Final status of pending extensions will be sent to this URL. - */ - webhookUrl?: string - /* - * Array of AI tags to remove from the asset. - */ - removeAITags?: string[]; - /* - * A key-value data to be associated with the asset. To unset a key, send null value for that key. Before setting any custom metadata on an asset you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /** - * Configure the publication status of a file and its versions. - */ - publish?: { - isPublished: boolean; - includeFileVersions?: boolean; - }; -} - -/** - * - * File object. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api#file-object-structure} - */ -export interface FileObject { - /** - * The unique fileId of the uploaded file. - */ - fileId: string; - /** - * Type of item. It can be either file, file-version or folder. - */ - type: "file" | "file-version"; - /** - * Name of the file or folder. - */ - name: string; - /** - * The relative path of the file. In case of image, you can use this - * path to construct different transformations. - */ - filePath: string; - /** - * Array of tags associated with the image. If no tags are set, it will be null. - */ - tags?: string[] | null; - /** - * Is the file marked as private. It can be either true or false. - */ - isPrivateFile: boolean; - /** - * Value of custom coordinates associated with the image in format x,y,width,height. - * If customCoordinates are not defined then it is null. - */ - customCoordinates: string | null; - /** - * A publicly accessible URL of the file. - */ - url: string; - /** - * In case of an image, a small thumbnail URL. - */ - thumbnail: string; - /** - * The type of file, it could be either image or non-image. - */ - fileType: FileType; - /* - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. - */ - AITags?: AITagItem[]; - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } - /* - * Consolidated embedded metadata associated with the file. It includes exif, iptc, and xmp data. - */ - embeddedMetadata?: EmbeddedMetadataValues | null; - /* - * A key-value data associated with the asset. Before setting any custom metadata on an asset, you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /* - * Size of the file in bytes - */ - size: number; - /* - * The date and time when the file was first uploaded. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - createdAt: string; - /* - * The date and time when the file was last updated. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - updatedAt: string; - /* - * Height of the image in pixels (Only for images) - */ - height: number; - /* - * Width of the image in pixels (Only for Images) - */ - width: number; - /* - * A boolean indicating if the image has an alpha layer or not. - */ - hasAlpha: boolean; - /* - * MIME Type of the file. For example - image/jpeg - */ - mime?: string; - /** - * An object containing the file or file version's id (versionId) and name. - */ - versionInfo?: { name: string; id: string }; -} - -/** - * - * Folder object. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api#file-object-structure} - */ -export interface FolderObject { - /** - * The unique fileId of the folder. - */ - folderId: string; - /** - * Type of item. It can be either file, file-version or folder. - */ - type: "folder"; - /** - * Name of the file or folder. - */ - name: string; - /** - * The relative path of the folder. - */ - folderPath: string; - /* - * The date and time when the folder was first created. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - createdAt: string; - /* - * The date and time when the folder was last updated. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - updatedAt: string; -} - -export interface FileVersionDetailsOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} diff --git a/libs/interfaces/FileFormat.ts b/libs/interfaces/FileFormat.ts deleted file mode 100644 index aaa9bc90..00000000 --- a/libs/interfaces/FileFormat.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @see {@link https://help.imagekit.io/en/articles/2434102-image-format-support-in-imagekit-for-resizing-compression-and-static-file-delivery} - */ -export type FileFormat = - | "jpg" - | "png" - | "gif" - | "svg" - | "webp" - | "pdf" - | "js" - | "css" - | "txt" - | "mp4" - | "webm" - | "mov" - | "swf" - | "ts" - | "m3u8" - | string; diff --git a/libs/interfaces/FileMetadata.ts b/libs/interfaces/FileMetadata.ts deleted file mode 100644 index 642310fe..00000000 --- a/libs/interfaces/FileMetadata.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { FileFormat } from "./FileFormat"; - -/** - * Response when getting image exif, pHash and other metadata for uploaded files in ImageKit.io media library using this API. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files} - */ -export interface FileMetadataResponse { - height: number; - width: number; - size: number; - format: FileFormat; - hasColorProfile: boolean; - quality: number; - density: number; - hasTransparency: boolean; - /** - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#perceptual-hash-phash} - */ - pHash: string; - /** - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#exif} - */ - exif: { - image: { - Make: string; - Model: string; - Orientation: number; - XResolution: number; - YResolution: number; - ResolutionUnit: number; - Software: string; - ModifyDate: string; - YCbCrPositioning: number; - ExifOffset: number; - GPSInfo: number; - }; - thumbnail: { - Compression: number; - XResolution: number; - YResolution: number; - ResolutionUnit: number; - ThumbnailOffset: number; - ThumbnailLength: number; - }; - exif: { - ExposureTime: number; - FNumber: number; - ExposureProgram: number; - ISO: number; - ExifVersion: string; - DateTimeOriginal: string; - CreateDate: string; - ShutterSpeedValue: number; - ApertureValue: number; - ExposureCompensation: number; - MeteringMode: number; - Flash: number; - FocalLength: number; - SubSecTime: string; - SubSecTimeOriginal: string; - SubSecTimeDigitized: string; - FlashpixVersion: string; - ColorSpace: number; - ExifImageWidth: number; - ExifImageHeight: number; - InteropOffset: number; - FocalPlaneXResolution: number; - FocalPlaneYResolution: number; - FocalPlaneResolutionUnit: number; - CustomRendered: number; - ExposureMode: number; - WhiteBalance: number; - SceneCaptureType: number; - }; - gps: { - GPSVersionID: number[]; - }; - interoperability: { - InteropIndex: string; - InteropVersion: string; - }; - makernote: { [key: string]: string }; - }; -} diff --git a/libs/interfaces/FileType.ts b/libs/interfaces/FileType.ts deleted file mode 100644 index f6b6cb70..00000000 --- a/libs/interfaces/FileType.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Type of files to include in result set. Accepts three values: - * all - include all types of files in result set - * image - only search in image type files - * non-image - only search in files which are not image, e.g., JS or CSS or video files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - */ -export type FileType = "all" | "image" | "non-image"; diff --git a/libs/interfaces/FileVersion.ts b/libs/interfaces/FileVersion.ts deleted file mode 100644 index b2c489e1..00000000 --- a/libs/interfaces/FileVersion.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface DeleteFileVersionOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} - -export interface RestoreFileVersionOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} \ No newline at end of file diff --git a/libs/interfaces/IKCallback.ts b/libs/interfaces/IKCallback.ts deleted file mode 100644 index b33ae426..00000000 --- a/libs/interfaces/IKCallback.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IKCallback { - (err: E | null, response: T): void; - (err: E, response: T | null): void; -} diff --git a/libs/interfaces/IKResponse.ts b/libs/interfaces/IKResponse.ts deleted file mode 100644 index a53ca4fb..00000000 --- a/libs/interfaces/IKResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -interface ResponseMetadata { - statusCode: number; - headers: Record; -} - -type IKResponse = T extends Error - ? T & { $ResponseMetadata?: ResponseMetadata } - : T & { $ResponseMetadata: ResponseMetadata }; - -export default IKResponse; diff --git a/libs/interfaces/ImageKitOptions.ts b/libs/interfaces/ImageKitOptions.ts deleted file mode 100644 index 122a4de2..00000000 --- a/libs/interfaces/ImageKitOptions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TransformationPosition } from "."; - -export interface ImageKitOptions { - uploadEndpoint?: string, - publicKey: string; - privateKey: string; - urlEndpoint: string; - transformationPosition?: TransformationPosition; -} diff --git a/libs/interfaces/ListFile.ts b/libs/interfaces/ListFile.ts deleted file mode 100644 index bba9321d..00000000 --- a/libs/interfaces/ListFile.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { FileObject, FolderObject } from "./FileDetails"; -import { FileType } from "./FileType"; - -/** - * List and search files options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - */ - -export interface ListFileOptions { - /** - * Folder path if you want to limit the search within a specific folder. For example, /sales-banner/ will only search in folder sales-banner. - */ - path?: string; - /** - * Type of files to include in result set. Accepts three values: - * all - include all types of files in result set - * image - only search in image type files - * non-image - only search in files which are not image, e.g., JS or CSS or video files. - */ - fileType?: FileType; - /** - * Comma-separated list of tags. Files matching any of the tags are included in result response. If no tag is matched, the file is not included in result set. - */ - tags?: string | string[]; - /** - * Whether to include folders in search results or not. By default only files are searched. - * Accepts true and false. If this is set to true then tags and fileType parameters are ignored. - */ - includeFolder?: boolean; - /** - * The name of the file or folder. - */ - name?: string; - /** - * The maximum number of results to return in response: - * Minimum value - 1 - * Maximum value - 1000 - * Default value - 1000 - */ - limit?: number; - /** - * The number of results to skip before returning results. - * Minimum value - 0 - * Default value - 0 - */ - skip?: number; - /** - * You can sort based on the following fields: - * - name - ASC_NAME or DESC_NAME - * - createdAt - ASC_CREATED or DESC_CREATED - * - updatedAt - ASC_UPDATED or DESC_UPDATED - * - height - ASC_HEIGHT or DESC_HEIGHT - * - width - ASC_WIDTH or DESC_WIDTH - * - size - ASC_SIZE or DESC_SIZE - */ - sort?: string; - /** - * Limit search to either file or folder. Pass all to include both files and folders in search results. - * Default value - `file` - */ - type?: string; - /** - * Query string in a Lucene-like query language. Learn more about the query expression later in this section. - * Note: When the searchQuery parameter is present, the following query parameters will have no effect on the result: - * 1. tags - * 2. type - * 3. name - */ - searchQuery?: string; -} - -/** - * - * List and search response - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files#response-structure-and-status-code-application-json} - */ -export type ListFileResponse = Array; diff --git a/libs/interfaces/MoveFile.ts b/libs/interfaces/MoveFile.ts deleted file mode 100644 index 1a26a162..00000000 --- a/libs/interfaces/MoveFile.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Move file API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-file} - */ -export interface MoveFileOptions { - /** - * The full path of the file you want to move. For example - /path/to/file.jpg - */ - sourceFilePath: string; - /** - * Full path to the folder you want to move the above file into. For example - /folder/to/move/into/ - */ - destinationPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/MoveFolder.ts b/libs/interfaces/MoveFolder.ts deleted file mode 100644 index 2361a6ed..00000000 --- a/libs/interfaces/MoveFolder.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Response when moving folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * On success, you will receive a jobId which can be used to get the move operation's status. - */ -export interface MoveFolderResponse { - jobId: string; -} - -/** - * Error when moving folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * If no files or folders are found at specified sourceFolderPath then a error is returned. - */ -export interface MoveFolderError extends Error { - help: string; - message: string; - reason: string; -} - - -/** - * Move folder API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - */ -export interface MoveFolderOptions { - /** - * The full path to the source folder you want to move. For example - /path/of/source/folder. - */ - sourceFolderPath: string; - /** - * Full path to the destination folder where you want to move the source folder into. For example - /path/of/destination/folder. - */ - destinationPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/PurgeCache.ts b/libs/interfaces/PurgeCache.ts deleted file mode 100644 index c7b0e386..00000000 --- a/libs/interfaces/PurgeCache.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Response when purging CDN and ImageKit.io internal cache - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache#response-structure-and-status-code} - */ - -export interface PurgeCacheResponse { - /** - * requestId can be used to fetch the status of submitted purge request. - */ - requestId: string; -} - -/** - * Response when getting the status of submitted purge request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache-status#understanding-response} - */ - -export interface PurgeCacheStatusResponse { - /** - * Pending - The request has been successfully submitted, and purging is in progress. - * Complete - The purge request has been successfully completed. And now you should get a fresh object. - * Check the Age header in response to confirm this. - */ - status: "Pending" | "Completed"; -} diff --git a/libs/interfaces/Rename.ts b/libs/interfaces/Rename.ts deleted file mode 100644 index 98b338df..00000000 --- a/libs/interfaces/Rename.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Response when rename file - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - */ -export interface RenameFileResponse { - /** - * When purgeCache is set to true - */ - purgeRequestId?: string; -} - -/** - * Response when rename file - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - */ -export interface RenameFileOptions { - /** - * The full path of the file you want to rename. For example - /path/to/file.jpg - */ - filePath: string; - /** - * The new name of the file. A filename can contain: - - Alphanumeric Characters: a-z, A-Z, 0-9 (including Unicode letters, marks, and numerals in other languages). - - Special Characters: ., _, and -. Any other character, including space, will be replaced by _. - */ - newFileName: string - /** - * Option to purge cache for the old file URL. When set to true, it will internally issue a purge cache request on CDN to remove cached content on the old URL. - */ - purgeCache: boolean -} diff --git a/libs/interfaces/Transformation.ts b/libs/interfaces/Transformation.ts deleted file mode 100644 index a2990f66..00000000 --- a/libs/interfaces/Transformation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SupportedTransformsParam } from "../constants/supportedTransforms"; - -export type TransformationPosition = "path" | "query"; - -export type Transformation = Partial< - | { - [key in SupportedTransformsParam]: string | boolean | number; - } - | { [key: string]: string | boolean | number } ->; \ No newline at end of file diff --git a/libs/interfaces/UploadOptions.ts b/libs/interfaces/UploadOptions.ts deleted file mode 100644 index 47389b21..00000000 --- a/libs/interfaces/UploadOptions.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ReadStream } from "fs"; -import { Extension } from "./FileDetails"; - -interface TransformationObject { - type: "transformation"; - value: string; -} -interface GifToVideoOrThumbnailObject { - type: "gif-to-video" | "thumbnail"; - value?: string; -} - -interface AbsObject { - type: "abs"; - value: string; - protocol: "hls" | "dash"; -} - -type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; - -interface Transformation{ - pre?: string - post?: PostTransformation[] -} - -/** - * Options used when uploading a file - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload#request-structure-multipart-form-data} - */ -export interface UploadOptions { - /** - * This field accepts three kinds of values: - * - binary - You can send the content of the file as binary. This is used when a file is being uploaded from the browser. - * - base64 - Base64 encoded string of file content. - * - url - URL of the file from where to download the content before uploading. - * Downloading file from URL might take longer, so it is recommended that you pass the binary or base64 content of the file. - * Pass the full URL, for example - https://www.example.com/rest-of-the-image-path.jpg. - */ - file: string | Buffer | ReadStream; - /** - * The name with which the file has to be uploaded. - * The file name can contain: - * - Alphanumeric Characters: a-z , A-Z , 0-9 - * - Special Characters: . _ and - - * Any other character including space will be replaced by _ - */ - fileName: string; - /** - * Whether to use a unique filename for this file or not. - * - Accepts true or false. - * - If set true, ImageKit.io will add a unique suffix to the filename parameter to get a unique filename. - * - If set false, then the image is uploaded with the provided filename parameter and any existing file with the same name is replaced. - * Default value - true - */ - useUniqueFileName?: boolean; - /** - * Set the tags while uploading the file. - * - Comma-separated value of tags in format tag1,tag2,tag3. For example - t-shirt,round-neck,men - * - The maximum length of all characters should not exceed 500. - * - % is not allowed. - * - If this field is not specified and the file is overwritten then the tags will be removed. - */ - tags?: string | string[]; - /** - * The folder path (e.g. /images/folder/) in which the image has to be uploaded. If the folder(s) didn't exist before, a new folder(s) is created. - * The folder name can contain: - * - Alphanumeric Characters: a-z , A-Z , 0-9 - * - Special Characters: / _ and - - * - Using multiple / creates a nested folder. - * Default value - / - */ - folder?: string; - /** - * Whether to mark the file as private or not. This is only relevant for image type files. - * - Accepts true or false. - * - If set true, the file is marked as private which restricts access to the original image URL and unnamed image transformations without signed URLs. - * Without the signed URL, only named transformations work on private images - * Default value - false - */ - isPrivateFile?: boolean; - /** - * Define an important area in the image. This is only relevant for image type files. - * To be passed as a string with the x and y coordinates of the top-left corner, and width and height of the area of interest in format x,y,width,height. For example - 10,10,100,100 - * Can be used with fo-customtransformation. - * If this field is not specified and the file is overwritten, then customCoordinates will be removed. - */ - customCoordinates?: string; - /** - * Comma-separated values of the fields that you want ImageKit.io to return in response. - * - * For example, set the value of this field to tags,customCoordinates,isPrivateFile,metadata to get value of tags, customCoordinates, isPrivateFile , and metadata in the response. - */ - responseFields?: string | string[]; - /* - * Object with array of extensions to be processed on the image. - */ - extensions?: Extension; - /* - * Final status of pending extensions will be sent to this URL. - */ - webhookUrl?: string; - overwriteFile?: boolean; - overwriteAITags?: boolean; - overwriteTags?: boolean; - overwriteCustomMetadata?: boolean; - customMetadata?: { - [key: string]: string | number | boolean | Array; - }, - transformation?: Transformation - /** - * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. - */ - checks?: string - /** - * Optional. Determines whether the file should be uploaded as published. - * If set to false, the file will be marked as unpublished, restricting access to the file through the media library only. - * Files in draft or unpublished states can only be publicly accessed after they are published. - */ - isPublished?: boolean -} diff --git a/libs/interfaces/UploadResponse.ts b/libs/interfaces/UploadResponse.ts deleted file mode 100644 index 9ca44376..00000000 --- a/libs/interfaces/UploadResponse.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { AITagItem, CMValues, EmbeddedMetadataValues } from "./FileDetails"; -import { FileMetadataResponse } from "./FileMetadata"; -import { FileType } from "./FileType"; - -/** - * Response from uploading a file - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload#response-code-and-structure-json} - */ -export interface UploadResponse { - /** - * Unique fileId. Store this fileld in your database, as this will be used to perform update action on this file. - */ - fileId: string; - /** - * The name of the uploaded file. - */ - name: string; - /** - * The URL of the file. - */ - url: string; - /** - * In case of an image, a small thumbnail URL. - */ - thumbnailUrl: string; - /** - * Height of the uploaded image file. Only applicable when file type is image. - */ - height: number; - /** - * Width of the uploaded image file. Only applicable when file type is image. - */ - width: number; - /** - * Size of the uploaded file in bytes. - */ - size: number; - /** - * Type of file. It can either be image or non-image. - */ - fileType: FileType; - /** - * The path of the file uploaded. It includes any folder that you specified while uploading. - */ - filePath: string; - /** - * Array of tags associated with the image. - */ - tags?: string[]; - /** - * Is the file marked as private. It can be either true or false. - */ - isPrivateFile: boolean; - /** - * Value of custom coordinates associated with the image in format x,y,width,height. - */ - customCoordinates: string | null; - /** - * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. - */ - metadata?: FileMetadataResponse; - /* - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. - */ - AITags?: AITagItem[]; - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } - /* - * Consolidated embedded metadata associated with the file. It includes exif, iptc, and xmp data. - */ - embeddedMetadata?: EmbeddedMetadataValues | null; - /* - * A key-value data associated with the asset. Before setting any custom metadata on an asset, you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /** - * Is the file published or in draft state. It can be either true or false. - */ - isPublished?: boolean -} diff --git a/libs/interfaces/UrlOptions.ts b/libs/interfaces/UrlOptions.ts deleted file mode 100644 index d3dc6ac7..00000000 --- a/libs/interfaces/UrlOptions.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { TransformationPosition } from "."; -import { Transformation } from "./Transformation"; - -export interface UrlOptionsBase { - /** - * An array of objects specifying the transformations to be applied in the URL. - * The transformation name and the value should be specified as a key-value pair in each object. - * @see {@link https://docs.imagekit.io/features/image-transformations/chained-transformations} - */ - transformation?: Array; - /** - * Default value is path that places the transformation string as a path parameter in the URL. - * Can also be specified as query which adds the transformation string as the query parameter tr in the URL. - * If you use src parameter to create the URL, then the transformation string is always added as a query parameter. - */ - transformationPosition?: TransformationPosition; - /** - * These are the other query parameters that you want to add to the final URL. - * These can be any query parameters and not necessarily related to ImageKit. - * Especially useful, if you want to add some versioning parameter to your URLs. - */ - queryParameters?: { [key: string]: string }; - /** - * The base URL to be appended before the path of the image. - * If not specified, the URL Endpoint specified at the time of SDK initialization is used. - */ - urlEndpoint?: string; - /** - * Default is false. If set to true, the SDK generates a signed image URL adding the image signature to the image URL. - * If you are creating URL using src parameter instead of path then do correct urlEndpoint for this to work. - * Otherwise returned URL will have wrong signature. - */ - signed?: boolean; - /** - * Meant to be used along with the signed parameter to specify the time in seconds from now when the URL should expire. - * If specified, the URL contains the expiry timestamp in the URL and the image signature is modified accordingly. - */ - expireSeconds?: number; -} - -export interface UrlOptionsSrc extends UrlOptionsBase { - /** - * Conditional. This is the complete URL of an image already mapped to ImageKit. - * For example, https://ik.imagekit.io/your_imagekit_id/endpoint/path/to/image.jpg. - * Either the path or src parameter need to be specified for URL generation. - */ - src: string; - path?: never; -} - -export interface UrlOptionsPath extends UrlOptionsBase { - /** - * Conditional. This is the path at which the image exists. - * For example, /path/to/image.jpg. Either the path or src parameter need to be specified for URL generation. - */ - path: string; - src?: never; -} - -/** - * Options for generating an URL - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#url-generation} - */ -export type UrlOptions = UrlOptionsSrc | UrlOptionsPath; diff --git a/libs/interfaces/index.ts b/libs/interfaces/index.ts deleted file mode 100644 index 538a897d..00000000 --- a/libs/interfaces/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ImageKitOptions } from "./ImageKitOptions"; -import { Transformation, TransformationPosition } from "./Transformation"; -import { UploadOptions } from "./UploadOptions"; -import { UploadResponse } from "./UploadResponse"; -import { FileType } from "./FileType"; -import { UrlOptions } from "./UrlOptions"; -import { ListFileOptions, ListFileResponse } from "./ListFile"; -import { CopyFileOptions } from "./CopyFile"; -import { MoveFileOptions } from "./MoveFile"; -import { CreateFolderOptions } from "./CreateFolder"; -import { FileDetailsOptions, FileVersionDetailsOptions, FileObject, FolderObject } from "./FileDetails"; -import { FileMetadataResponse } from "./FileMetadata"; -import { PurgeCacheResponse, PurgeCacheStatusResponse } from "./PurgeCache"; -import { BulkDeleteFilesResponse, BulkDeleteFilesError } from "./BulkDeleteFiles"; -import { CopyFolderOptions, CopyFolderResponse, CopyFolderError } from "./CopyFolder"; -import { MoveFolderOptions, MoveFolderResponse, MoveFolderError } from "./MoveFolder"; -import { DeleteFileVersionOptions, RestoreFileVersionOptions } from "./FileVersion" -import { CreateCustomMetadataFieldOptions, CustomMetadataField, UpdateCustomMetadataFieldOptions, GetCustomMetadataFieldsOptions } from "./CustomMetatadaField" -import { RenameFileOptions, RenameFileResponse } from "./Rename" -import { - WebhookEvent, - WebhookEventVideoTransformationAccepted, - WebhookEventVideoTransformationReady, - WebhookEventVideoTransformationError, - WebhookEventUploadPreTransformationSuccess, - WebhookEventUploadPreTransformationError, - WebhookEventUploadPostTransformationSuccess, - WebhookEventUploadPostTransformationError -} from "./webhookEvent"; - -type FinalUrlOptions = ImageKitOptions & UrlOptions; // actual options used to construct url - -export type { - ImageKitOptions, - Transformation, - TransformationPosition, - UploadOptions, - UploadResponse, - FileType, - UrlOptions, - FinalUrlOptions, - ListFileOptions, - ListFileResponse, - FileDetailsOptions, - FileVersionDetailsOptions, - FileObject, - FolderObject, - FileMetadataResponse, - PurgeCacheResponse, - PurgeCacheStatusResponse, - BulkDeleteFilesResponse, - BulkDeleteFilesError, - CopyFolderResponse, - CopyFolderError, - MoveFolderResponse, - MoveFolderError, - CopyFileOptions, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - CreateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - RenameFileOptions, - RenameFileResponse, - WebhookEvent, - WebhookEventVideoTransformationAccepted, - WebhookEventVideoTransformationReady, - WebhookEventVideoTransformationError, - WebhookEventUploadPostTransformationSuccess, - WebhookEventUploadPostTransformationError, - WebhookEventUploadPreTransformationSuccess, - WebhookEventUploadPreTransformationError -}; -export type { IKCallback } from "./IKCallback"; diff --git a/libs/interfaces/webhookEvent.ts b/libs/interfaces/webhookEvent.ts deleted file mode 100644 index 194afdcd..00000000 --- a/libs/interfaces/webhookEvent.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { UploadResponse } from "./UploadResponse"; - -type Asset = { - url: string; -}; - -type TransformationOptions = { - video_codec: string; - audio_codec: string; - auto_rotate: boolean; - quality: number; - format: string; -}; - -interface WebhookEventBase { - type: string; - id: string; - created_at: string; // Date -} - -/** WebhookEvent for "video.transformation.*" type */ -interface WebhookEventVideoTransformationBase extends WebhookEventBase { - request: { - x_request_id: string; - url: string; - user_agent: string; - }; -} - -export interface WebhookEventVideoTransformationAccepted extends WebhookEventVideoTransformationBase { - type: "video.transformation.accepted"; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - }; - }; -} - -export interface WebhookEventVideoTransformationReady extends WebhookEventVideoTransformationBase { - type: "video.transformation.ready"; - timings: { - donwload_duration: number; - encoding_duration: number; - }; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - output: { - url: string; - video_metadata: { - duration: number; - width: number; - height: number; - bitrate: number; - }; - }; - }; - }; -} - -export interface WebhookEventVideoTransformationError extends WebhookEventVideoTransformationBase { - type: "video.transformation.error"; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - error: { - reason: string; - }; - }; - }; -} - -type TransformationType = "transformation" | "abs" | "gif-to-video" | "thumbnail"; - -interface PreTransformationBase { - id: string; - created_at: string; - request: { - x_request_id: string; - transformation: string; - }; -} - -interface PostTransformationBase { - id: string; - created_at: string; - request: { - x_request_id: string; - transformation: { - type: TransformationType; - value?: string; - protocol?: 'hls' | 'dash'; - }; - }; -} - -export interface WebhookEventUploadPreTransformationSuccess extends PreTransformationBase { - type: "upload.pre-transform.success"; - data: UploadResponse; -} - -export interface WebhookEventUploadPreTransformationError extends PostTransformationBase { - type: "upload.pre-transform.error"; - data: { - name: string; - path: string; - transformation: { - error: { - reason: string; - }; - }; - }; -} - -export interface WebhookEventUploadPostTransformationSuccess extends PostTransformationBase { - type: "upload.post-transform.success"; - data: { - fileId: string; - url: string; - name: string; - }; -} - -export interface WebhookEventUploadPostTransformationError extends PostTransformationBase { - type: "upload.post-transform.error"; - data: { - fileId: string; - url: string; - name: string; - path: string; - transformation: { - error: { - reason: string; - }; - }; - }; -} - -export type WebhookEvent = - | WebhookEventVideoTransformationAccepted - | WebhookEventVideoTransformationReady - | WebhookEventVideoTransformationError - | WebhookEventUploadPreTransformationSuccess - | WebhookEventUploadPreTransformationError - | WebhookEventUploadPostTransformationSuccess - | WebhookEventUploadPostTransformationError - | Object; diff --git a/libs/manage/cache.ts b/libs/manage/cache.ts deleted file mode 100644 index a3bb4ef7..00000000 --- a/libs/manage/cache.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { ImageKitOptions, PurgeCacheResponse, PurgeCacheStatusResponse } from "../interfaces"; - -const purgeCache = function (url: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!url && !url.length) { - respond(true, errorMessages.CACHE_PURGE_URL_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/purge", - method: "POST", - json: { - url: url, - }, - }; - - request(requestOptions, defaultOptions, callback); -}; - -const getPurgeCacheStatus = function ( - requestId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!requestId && !requestId.length) { - respond(true, errorMessages.CACHE_PURGE_STATUS_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/purge/" + requestId, - method: "GET", - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { purgeCache, getPurgeCacheStatus }; diff --git a/libs/manage/custom-metadata-field.ts b/libs/manage/custom-metadata-field.ts deleted file mode 100644 index 752ab080..00000000 --- a/libs/manage/custom-metadata-field.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { - ImageKitOptions, - CreateCustomMetadataFieldOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, -} from "../interfaces"; - -const create = function (createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - const { name, label, schema } = createCustomMetadataFieldOptions; - if (!name || !name.length) { - respond(true, errorMessages.CMF_NAME_MISSING, callback); - return; - } - - if (!label || !label.length) { - respond(true, errorMessages.CMF_LABEL_MISSING, callback); - return; - } - - if (!schema) { - respond(true, errorMessages.CMF_SCHEMA_MISSING, callback); - return; - } - - if (!schema.type) { - respond(true, errorMessages.CMF_SCHEMA_INVALID, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/customMetadataFields", - method: "POST", - json: { - name, - label, - schema - }, - }; - - request(requestOptions, defaultOptions, callback); -}; - -const list = function ( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { includeDeleted = false } = getCustomMetadataFieldsOptions || {}; - var requestOptions = { - url: "https://api.imagekit.io/v1/customMetadataFields", - method: "GET", - qs: { includeDeleted } - }; - - request(requestOptions, defaultOptions, callback); -}; - -const update = function (fieldId: string, updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!fieldId || typeof fieldId !== "string" || !fieldId.length) { - respond(true, errorMessages.CMF_FIELD_ID_MISSING, callback); - return; - } - - const { label, schema } = updateCustomMetadataFieldOptions; - if (!label && !schema) { - respond(true, errorMessages.CMF_LABEL_SCHEMA_MISSING, callback); - return; - } - - var requestBody: UpdateCustomMetadataFieldOptions = {}; - if (label) requestBody.label = label; - if (schema) requestBody.schema = schema; - - var requestOptions = { - url: `https://api.imagekit.io/v1/customMetadataFields/${fieldId}`, - method: "PATCH", - json: requestBody - }; - - request(requestOptions, defaultOptions, callback); -}; - -const deleteField = function ( - fieldId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fieldId || typeof fieldId !== "string" || !fieldId.length) { - respond(true, errorMessages.CMF_FIELD_ID_MISSING, callback); - return; - } - var requestOptions = { - url: `https://api.imagekit.io/v1/customMetadataFields/${fieldId}`, - method: "DELETE", - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { create, list, update, deleteField }; diff --git a/libs/manage/file.ts b/libs/manage/file.ts deleted file mode 100644 index 5ee18da7..00000000 --- a/libs/manage/file.ts +++ /dev/null @@ -1,699 +0,0 @@ -import { isObject } from 'lodash'; - -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { - ImageKitOptions, - ListFileOptions, - ListFileResponse, - FileDetailsOptions, - FileVersionDetailsOptions, - FileObject, - FileMetadataResponse, - BulkDeleteFilesResponse, - BulkDeleteFilesError, - CopyFileOptions, - CopyFolderResponse, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - RenameFileOptions, - RenameFileResponse, -} from "../interfaces"; -import ImageKit from "../.."; - -/* - Delete a file -*/ -const deleteFile = function (fileId: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId, - method: "DELETE" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Delete a file version -*/ -const deleteFileVersion = function (deleteFileVersionOptions: DeleteFileVersionOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - const { fileId, versionId } = deleteFileVersionOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}`, - method: "DELETE" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Restore a file version as the current version -*/ -const restoreFileVersion = function ( - restoreFileVersionOptions: RestoreFileVersionOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback) { - const { fileId, versionId } = restoreFileVersionOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}/restore`, - method: "PUT" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Get Metadata of a file -*/ -const getMetadata = function ( - fileIdOrURL: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileIdOrURL || fileIdOrURL.trim() == "") { - respond(true, errorMessages.FILE_ID_OR_URL_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileIdOrURL + "/metadata", - method: "GET" - }; - - // In case of URL change the endopint - if (fileIdOrURL.indexOf("http") === 0) { - requestOptions = { - url: `https://api.imagekit.io/v1/metadata?url=${fileIdOrURL}`, - method: "GET" - }; - } - - request(requestOptions, defaultOptions, callback); -}; - -/* - Get Details of a file -*/ -const getDetails = function ( - fileId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId + "/details", - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Get Details of a file version -*/ -const getFileVersionDetails = function ( - fileDetailsOptions: FileVersionDetailsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { fileId, versionId } = fileDetailsOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}`, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Update file details -*/ -const updateDetails = function ( - fileId: string, - updateData: FileDetailsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!isObject(updateData)) { - respond(true, errorMessages.UPDATE_DATA_MISSING, callback); - return; - } - - var data = {}; - data = { - tags: updateData.tags, - customCoordinates: updateData.customCoordinates, - extensions: updateData.extensions, - webhookUrl: updateData.webhookUrl, - customMetadata: updateData.customMetadata, - }; - - if (updateData.publish) - data = { - ...data, - publish: updateData.publish, - }; - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId + "/details", - method: "PATCH", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - List files -*/ -const listFiles = function ( - listOptions: ListFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (listOptions && !isObject(listOptions)) { - respond(true, errorMessages.INVALID_LIST_OPTIONS, callback); - return; - } - - if (listOptions && listOptions.tags && Array.isArray(listOptions.tags) && listOptions.tags.length) { - listOptions.tags = listOptions.tags.join(","); - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/`, - method: "GET", - qs: listOptions || {} - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Get all versions of an asset -*/ -const getFilesVersions = function ( - fileId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions`, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Bulk Delete By FileIds -*/ -const bulkDeleteFiles = function ( - fileIdArray: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - const data = { - fileIds: fileIdArray, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/batch/deleteByFileIds", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Add tags in bulk -*/ -const bulkAddTags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - tags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/addTags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Remove tags in bulk -*/ -const bulkRemoveTags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - tags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/removeTags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Remove AI tags in bulk -*/ -const bulkRemoveAITags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - AITags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/removeAITags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Copy file -*/ -const copyFile = function ( - copyFileOptions: CopyFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFilePath, destinationPath, includeFileVersions = false } = copyFileOptions; - - if (typeof sourceFilePath !== "string" || sourceFilePath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FILE_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - if (typeof includeFileVersions !== "boolean") { - respond(true, errorMessages.INVALID_INCLUDE_VERSION, callback); - return; - } - - const data = { - sourceFilePath, - destinationPath, - includeFileVersions - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/copy", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Move file -*/ -const moveFile = function ( - moveFileOptions: MoveFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFilePath, destinationPath } = moveFileOptions; - if (typeof sourceFilePath !== "string" || sourceFilePath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FILE_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - const data = { - sourceFilePath, - destinationPath - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/move", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Rename file -*/ -const renameFile = function ( - renameFileOptions: RenameFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { filePath, newFileName, purgeCache = false } = renameFileOptions; - if (typeof filePath !== "string" || filePath.length === 0) { - respond(true, errorMessages.INVALID_FILE_PATH, callback); - return; - } - - if (typeof newFileName !== "string" || newFileName.length === 0) { - respond(true, errorMessages.INVALID_NEW_FILE_NAME, callback); - return; - } - - if (typeof purgeCache !== "boolean") { - respond(true, errorMessages.INVALID_PURGE_CACHE, callback); - return; - } - - const data = { - filePath, - newFileName, - purgeCache - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/rename", - method: "PUT", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Copy Folder -*/ -const copyFolder = function ( - copyFolderOptions: CopyFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFolderPath, destinationPath, includeFileVersions = false } = copyFolderOptions; - if (typeof sourceFolderPath !== "string" || sourceFolderPath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FOLDER_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - if (typeof includeFileVersions !== "boolean") { - respond(true, errorMessages.INVALID_INCLUDE_VERSION, callback); - return; - } - - const data = { - sourceFolderPath, - destinationPath, - includeFileVersions - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/copyFolder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Move Folder -*/ -const moveFolder = function ( - moveFolderOptions: MoveFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFolderPath, destinationPath } = moveFolderOptions; - - if (typeof sourceFolderPath !== "string" || sourceFolderPath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FOLDER_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - const data = { - sourceFolderPath, - destinationPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/moveFolder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Create folder -*/ -const createFolder = function ( - createFolderOptions: CreateFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { folderName, parentFolderPath } = createFolderOptions; - if (typeof folderName !== "string" || folderName.length === 0) { - respond(true, errorMessages.INVALID_FOLDER_NAME, callback); - return; - } - - if (typeof parentFolderPath !== "string" || parentFolderPath.length === 0) { - respond(true, errorMessages.INVALID_PARENT_FOLDER_PATH, callback); - return; - } - - const data = { - folderName: folderName, - parentFolderPath: parentFolderPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/folder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Delete folder -*/ -const deleteFolder = function (folderPath: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (typeof folderPath !== "string" || folderPath.length === 0) { - respond(true, errorMessages.INVALID_FOLDER_PATH, callback); - return; - } - - const data = { - folderPath: folderPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/folder", - method: "DELETE", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Bulk job status -*/ -const getBulkJobStatus = function (jobId: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!jobId) { - respond(true, errorMessages.JOB_ID_MISSING, callback); - return; - } - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/" + jobId, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { - deleteFile, - getMetadata, - getDetails, - getFileVersionDetails, - updateDetails, - listFiles, - getFilesVersions, - bulkDeleteFiles, - deleteFileVersion, - restoreFileVersion, - bulkAddTags, - bulkRemoveTags, - bulkRemoveAITags, - copyFile, - moveFile, - renameFile, - copyFolder, - moveFolder, - createFolder, - deleteFolder, - getBulkJobStatus, -}; diff --git a/libs/manage/index.ts b/libs/manage/index.ts deleted file mode 100644 index 72f4ac34..00000000 --- a/libs/manage/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import cache from "./cache"; -import file from "./file"; -import customMetadataField from "./custom-metadata-field"; - -export default { - listFiles: file.listFiles, - getFileDetails: file.getDetails, - getFileVersions: file.getFilesVersions, - getFileVersionDetails: file.getFileVersionDetails, - updateFileDetails: file.updateDetails, - getFileMetadata: file.getMetadata, - deleteFile: file.deleteFile, - bulkDeleteFiles: file.bulkDeleteFiles, - deleteFileVersion: file.deleteFileVersion, - restoreFileVersion: file.restoreFileVersion, - bulkAddTags: file.bulkAddTags, - bulkRemoveTags: file.bulkRemoveTags, - bulkRemoveAITags: file.bulkRemoveAITags, - copyFile: file.copyFile, - moveFile: file.moveFile, - renameFile: file.renameFile, - copyFolder: file.copyFolder, - moveFolder: file.moveFolder, - createFolder: file.createFolder, - deleteFolder: file.deleteFolder, - getBulkJobStatus: file.getBulkJobStatus, - purgeCache: cache.purgeCache, - getPurgeCacheStatus: cache.getPurgeCacheStatus, - createCustomMetadataField: customMetadataField.create, - getCustomMetadataFields: customMetadataField.list, - updateCustomMetadataField: customMetadataField.update, - deleteCustomMetadataField: customMetadataField.deleteField, -}; diff --git a/libs/signature/index.ts b/libs/signature/index.ts deleted file mode 100644 index 8d17acec..00000000 --- a/libs/signature/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - Helper Modules -*/ -import { v4 as uuid } from "uuid"; -import crypto from "crypto"; -import { ImageKitOptions } from "../interfaces"; -var DEFAULT_TIME_DIFF = 60 * 30; - -const getAuthenticationParameters = function (token?: string, expire?: number, defaultOptions?: ImageKitOptions) { - var defaultExpire = parseInt(String(new Date().getTime() / 1000), 10) + DEFAULT_TIME_DIFF; - var authParameters = { - token: token || "", - expire: expire || 0, - signature: "", - }; - - if (!defaultOptions || !defaultOptions.privateKey) return authParameters; - - token = token || uuid(); - expire = expire || defaultExpire; - var signature = crypto - .createHmac("sha1", defaultOptions.privateKey) - .update(token + expire) - .digest("hex"); - - authParameters.token = token; - authParameters.expire = expire; - authParameters.signature = signature; - - return authParameters; -}; - -export default { getAuthenticationParameters }; diff --git a/libs/upload/index.ts b/libs/upload/index.ts deleted file mode 100644 index 7d1a8577..00000000 --- a/libs/upload/index.ts +++ /dev/null @@ -1,110 +0,0 @@ -import _ from "lodash"; -import errorMessages from "../constants/errorMessages"; -import respond from "../../utils/respond"; -import request from "../../utils/request"; -import { IKCallback } from "../interfaces/IKCallback"; -import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; -import FormData from "form-data"; - -type Modify = Omit & R; -type FormDataOptions = Modify< - UploadOptions, - { - file: string | Buffer | object; - useUniqueFileName: string; - isPrivateFile: string; - extensions?: string; - webhookUrl?: string; - overwriteFile?: string; - overwriteAITags?: string; - overwriteTags?: string; - overwriteCustomMetadata?: string; - customMetadata?: string; - } ->; - -export default function ( - uploadOptions: UploadOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -): void | Promise { - if (!_.isObject(uploadOptions)) { - respond(true, errorMessages.MISSING_UPLOAD_DATA, callback); - return; - } - - if (!uploadOptions.file) { - respond(true, errorMessages.MISSING_UPLOAD_FILE_PARAMETER, callback); - return; - } - - if (!uploadOptions.fileName) { - respond(true, errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER, callback); - return; - } - - if (uploadOptions.transformation) { - if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { - respond(true, errorMessages.INVALID_TRANSFORMATION, callback); - return; - } - if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { - respond(true, errorMessages.INVALID_PRE_TRANSFORMATION, callback); - return; - } - if (Object.keys(uploadOptions.transformation).includes("post")) { - if (Array.isArray(uploadOptions.transformation.post)) { - for (let transformation of uploadOptions.transformation.post) { - if (transformation.type === "abs" && !(transformation.protocol || transformation.value)) { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } else if (transformation.type === "transformation" && !transformation.value) { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } - } - } else { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } - } - } - - var formData = {} as FormDataOptions; - - const form = new FormData(); - - let key: keyof typeof uploadOptions; - for (key in uploadOptions) { - if (key) { - if (key == "file" && typeof uploadOptions.file != "string") { - // form.append('file', uploadOptions.file); - form.append('file', uploadOptions.file, String(uploadOptions.fileName)); - } else if (key == "tags" && Array.isArray(uploadOptions.tags)) { - form.append('tags', uploadOptions.tags.join(",")); - } else if (key == "responseFields" && Array.isArray(uploadOptions.responseFields)) { - form.append('responseFields', uploadOptions.responseFields.join(",")); - } else if (key == "extensions" && Array.isArray(uploadOptions.extensions)) { - form.append('extensions', JSON.stringify(uploadOptions.extensions)); - } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && - !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { - form.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); - } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && - uploadOptions.transformation !== null) { - form.append(key, JSON.stringify(uploadOptions.transformation)); - } else if (key === "checks" && uploadOptions.checks) { - form.append(key, uploadOptions.checks); - } else { - form.append(key, String(uploadOptions[key])); - } - } - } - - var requestOptions = { - url: defaultOptions.uploadEndpoint || "https://upload.imagekit.io/api/v1/files/upload", - method: "POST", - formData: form - }; - - request(requestOptions, defaultOptions, callback); -} diff --git a/libs/url/builder.ts b/libs/url/builder.ts deleted file mode 100644 index 87319978..00000000 --- a/libs/url/builder.ts +++ /dev/null @@ -1,179 +0,0 @@ -/* - Helper Modules -*/ -import { URLSearchParams, URL } from "url"; -import path from "path"; -import crypto from "crypto"; - -/* - Utils -*/ -import transformationUtils from "../../utils/transformation"; -import urlFormatter from "../../utils/urlFormatter"; - -/* - Interfaces -*/ -import { FinalUrlOptions, Transformation } from "../interfaces"; - -/* - Variables -*/ -const TRANSFORMATION_PARAMETER: string = "tr"; -const SIGNATURE_PARAMETER: string = "ik-s"; -const TIMESTAMP_PARAMETER: string = "ik-t"; -const DEFAULT_TIMESTAMP: string = "9999999999"; - -//used to check if special char is present in string (you'll need to encode it to utf-8 if it does) -const hasMoreThanAscii = (str: string) => { - return str.split('').some((char) => char.charCodeAt(0) > 127); -} - -const customEncodeURI = (str: string) => { - return str.includes("?") ? `${encodeURI(str.split("?")[0])}?${str.split("?")[1]}` : encodeURI(str); -}; - -export const encodeStringIfRequired = (str: string) => { - return hasMoreThanAscii(str) ? customEncodeURI(str) : str; -} - -const buildURL = function (opts: FinalUrlOptions): string { - var isSrcParameterUsedForURL: boolean = false; - - var urlObject: URL; - - if (opts.path) { - urlObject = new URL(opts.urlEndpoint) - } else if (opts.src) { - isSrcParameterUsedForURL = true; - urlObject = new URL(opts.src) - } else { - return ""; - } - - - var queryParameters = new URLSearchParams(urlObject.search || ""); - for (var i in opts.queryParameters) { - queryParameters.set(i, opts.queryParameters[i]); - } - - //Create Transformation String - var transformationString = constructTransformationString(opts.transformation); - if (transformationString) { - //force that if src parameter is being used for URL construction then the transformation - //string should be added only as a query parameter - if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { - queryParameters.set(TRANSFORMATION_PARAMETER, transformationString); - urlObject.pathname= `${urlObject.pathname}${opts.path||''}`; - } else { - urlObject.pathname = path.posix.join( - urlObject.pathname, - [TRANSFORMATION_PARAMETER, transformationString].join(transformationUtils.getChainTransformDelimiter()), - opts.path || '', - ); - } - } - else{ - urlObject.pathname= `${urlObject.pathname}${opts.path||''}`; - } - - urlObject.host = urlFormatter.removeTrailingSlash(urlObject.host); - urlObject.pathname = urlFormatter.addLeadingSlash(urlObject.pathname); - urlObject.search = queryParameters.toString(); - - /* - Signature String and Timestamp - If the url is constructed using src parameter instead of path then we still replace the urlEndpoint we have - But the user is responsible for passing correct urlEndpoint value - - Signature generation logic, let's assume: - urlEndpoint value = https://ik.imagekit.io/your_imagekit_id - expiryTimestamp 9999999999 - 1. Let the final URL construct e.g. https://ik.imagekit.io/your_imagekit_id/tr:w-400:rotate-91/sample/testing-file.jpg?param1=123 - 2. Now remove urlEndpoint from it i.e tr:w-400:rotate-91/sample/testing-file.jpg?param1=123 - 3. Append expiryTimestamp to above string and calcualte signature of this string i.e "tr:w-400:rotate-91/sample/testing-file.jpg?param1=1239999999999" - */ - var expiryTimestamp; - if (opts.signed === true) { - if (opts.expireSeconds) { - expiryTimestamp = getSignatureTimestamp(opts.expireSeconds); - } else { - expiryTimestamp = DEFAULT_TIMESTAMP; - } - - var intermediateURL = urlObject.href; - - var urlSignature = getSignature({ - privateKey: opts.privateKey, - url: intermediateURL, - urlEndpoint: opts.urlEndpoint, - expiryTimestamp: expiryTimestamp, - }); - - if (expiryTimestamp && expiryTimestamp != DEFAULT_TIMESTAMP) { - queryParameters.set(TIMESTAMP_PARAMETER, expiryTimestamp); - } - queryParameters.set(SIGNATURE_PARAMETER, urlSignature); - urlObject.search = queryParameters.toString(); - } - return urlObject.href; -}; - -function constructTransformationString(inputTransformation: Array | undefined) { - - const transformation = inputTransformation as Array<{ [key: string]: string | boolean | number }> | undefined; - if (!Array.isArray(transformation)) { - return ""; - } - - var parsedTransforms = []; - for (var i = 0, l = transformation.length; i < l; i++) { - var parsedTransformStep = []; - for (var key in transformation[i]) { - if(transformation[i][key] === undefined || transformation[i][key] === null ) - continue; - let transformKey = transformationUtils.getTransformKey(key); - if (!transformKey) { - transformKey = key; - } - - if (transformation[i][key] === "-") { - parsedTransformStep.push(transformKey); - } else if (key === "raw") { - parsedTransformStep.push(transformation[i][key]); - } else { - var value = String(transformation[i][key]); - if (transformKey === "di") { - value = urlFormatter.removeTrailingSlash(urlFormatter.removeLeadingSlash(value)); - if (value) value = value.replace(/\//g, "@@"); - } - parsedTransformStep.push([transformKey, value].join(transformationUtils.getTransformKeyValueDelimiter())); - } - } - parsedTransforms.push(parsedTransformStep.join(transformationUtils.getTransformDelimiter())); - } - - return parsedTransforms.join(transformationUtils.getChainTransformDelimiter()); -} - -function getSignatureTimestamp(seconds: number): string { - if (!seconds) return DEFAULT_TIMESTAMP; - - var sec = parseInt(String(seconds), 10); - if (!sec) return DEFAULT_TIMESTAMP; - - var currentTimestamp = parseInt(String(new Date().getTime() / 1000), 10); - return String(currentTimestamp + sec); -} - -export function getSignature(opts: any) { - if (!opts.privateKey || !opts.url || !opts.urlEndpoint) return ""; - var stringToSign = opts.url.replace(urlFormatter.addTrailingSlash(opts.urlEndpoint), "") + opts.expiryTimestamp; - stringToSign = encodeStringIfRequired(stringToSign); - return crypto.createHmac("sha1", opts.privateKey).update(stringToSign).digest("hex"); -} - -export default { - buildURL, - getSignature, -}; diff --git a/libs/url/index.ts b/libs/url/index.ts deleted file mode 100644 index 35cd2d50..00000000 --- a/libs/url/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - Helper Modules -*/ -import _ from "lodash"; -/* - Interfaces -*/ -import { UrlOptions, ImageKitOptions, FinalUrlOptions } from "../interfaces"; -/* - URL builder -*/ -import builder from "./builder"; - -export default function (urlOpts: UrlOptions, defaultOptions: ImageKitOptions): string { - var opts: FinalUrlOptions = _.extend({}, defaultOptions, urlOpts); - - return builder.buildURL(opts); -} diff --git a/package.json b/package.json index 40f220ed..d250a709 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,71 @@ { - "name": "imagekit", - "version": "6.0.0", + "name": "@imagekit/nodejs", + "version": "7.0.0", "description": "Offical NodeJS SDK for ImageKit.io integration", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "compile": "rm -rf dist/ & tsc -p tsconfig.json", - "test": "export NODE_ENV=test; nyc ./node_modules/mocha/bin/mocha --exit -t 40000 tests/*.js;ex=$?;unset NODE_ENV; exit $ex;", - "test-e2e": "sh test-e2e.sh; exit $?;", - "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov", - "prepack": "npm run compile" - }, - "author": "ImageKit Developer ", - "homepage": "https://imagekit.io", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/imagekit-developer/imagekit-nodejs.git" + "author": "Image Kit ", + "types": "dist/index.d.ts", + "main": "dist/index.js", + "type": "commonjs", + "repository": "github:imagekit-developer/imagekit-nodejs", + "license": "Apache-2.0", + "packageManager": "yarn@1.22.22", + "files": [ + "**/*" + ], + "private": false, + "publishConfig": { + "access": "public" }, - "bugs": { - "url": "https://github.com/imagekit-developer/imagekit-nodejs/issues" + "scripts": { + "test": "./scripts/test", + "build": "./scripts/build", + "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1", + "format": "./scripts/format", + "prepare": "if ./scripts/utils/check-is-in-git-install.sh; then ./scripts/build && ./scripts/utils/git-swap.sh; fi", + "tsn": "ts-node -r tsconfig-paths/register", + "lint": "./scripts/lint", + "fix": "./scripts/format" }, - "keywords": [ - "imagekit", - "nodejs", - "javascript", - "sdk", - "js", - "sdk", - "image", - "optimization", - "image", - "transformation", - "image", - "resize" - ], "dependencies": { - "axios": "^1.6.5", - "form-data": "^4.0.0", - "hamming-distance": "^1.0.0", - "lodash": "^4.17.15", - "tslib": "^2.4.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=12.0.0" + "standardwebhooks": "^1.0.0" }, "devDependencies": { - "@babel/cli": "^7.14.5", - "@babel/core": "^7.14.6", - "@babel/node": "^7.14.5", - "@babel/preset-env": "^7.14.5", - "@babel/preset-typescript": "^7.14.5", - "@babel/register": "^7.14.5", - "@types/chai": "^4.3.1", - "@types/lodash": "^4.14.170", - "@types/mocha": "^9.1.1", - "@types/node": "^15.12.2", - "@types/request": "^2.48.5", - "@types/sinon": "^10.0.12", - "@types/uuid": "^8.3.4", - "babel-plugin-replace-ts-export-assignment": "^0.0.2", - "chai": "^4.2.0", - "codecov": "^3.8.0", - "concurrently": "6.5.1", - "mocha": "^8.1.1", - "nock": "^13.2.7", - "nyc": "^15.1.0", - "sinon": "^9.2.0", - "typescript": "^4.3.2" + "@arethetypeswrong/cli": "^0.17.0", + "@swc/core": "^1.3.102", + "@swc/jest": "^0.2.29", + "@types/jest": "^29.4.0", + "@types/node": "^20.17.6", + "@typescript-eslint/eslint-plugin": "8.31.1", + "@typescript-eslint/parser": "8.31.1", + "eslint": "^9.20.1", + "eslint-plugin-prettier": "^5.4.1", + "eslint-plugin-unused-imports": "^4.1.4", + "iconv-lite": "^0.6.3", + "jest": "^29.4.0", + "prettier": "^3.0.0", + "publint": "^0.2.12", + "ts-jest": "^29.1.0", + "ts-node": "^10.5.0", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsconfig-paths": "^4.0.0", + "tslib": "^2.8.1", + "typescript": "5.8.3", + "typescript-eslint": "8.31.1" + }, + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./*.mjs": { + "default": "./dist/*.mjs" + }, + "./*.js": { + "default": "./dist/*.js" + }, + "./*": { + "import": "./dist/*.mjs", + "require": "./dist/*.js" + } } } diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md new file mode 100644 index 00000000..95170839 --- /dev/null +++ b/packages/mcp-server/README.md @@ -0,0 +1,363 @@ +# Image Kit TypeScript MCP Server + +## Installation + +### Building + +Because it's not published yet, clone the repo and build it: + +```sh +git clone git@github.com:imagekit-developer/imagekit-nodejs.git +cd imagekit-nodejs +./scripts/bootstrap +./scripts/build +``` + +### Running + +```sh +# set env vars as needed +export IMAGEKIT_PRIVATE_KEY="My Private Key" +export OPTIONAL_IMAGEKIT_IGNORES_THIS="My Password" +export IMAGEKIT_WEBHOOK_SECRET="My Webhook Secret" +node ./packages/mcp-server/dist/index.js +``` + +> [!NOTE] +> Once this package is [published to npm](https://www.stainless.com/docs/guides/publish), this will become: `npx -y imagekit-api-mcp` + +### Via MCP Client + +[Build the project](#building) as mentioned above. + +There is a partial list of existing clients at [modelcontextprotocol.io](https://modelcontextprotocol.io/clients). If you already +have a client, consult their documentation to install the MCP server. + +For clients with a configuration JSON, it might look something like this: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "node", + "args": ["/path/to/local/imagekit-nodejs/packages/mcp-server", "--client=claude", "--tools=dynamic"], + "env": { + "IMAGEKIT_PRIVATE_KEY": "My Private Key", + "OPTIONAL_IMAGEKIT_IGNORES_THIS": "My Password", + "IMAGEKIT_WEBHOOK_SECRET": "My Webhook Secret" + } + } + } +} +``` + +## Exposing endpoints to your MCP Client + +There are two ways to expose endpoints as tools in the MCP server: + +1. Exposing one tool per endpoint, and filtering as necessary +2. Exposing a set of tools to dynamically discover and invoke endpoints from the API + +### Filtering endpoints and tools + +You can run the package on the command line to discover and filter the set of tools that are exposed by the +MCP Server. This can be helpful for large APIs where including all endpoints at once is too much for your AI's +context window. + +You can filter by multiple aspects: + +- `--tool` includes a specific tool by name +- `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*` +- `--operation` includes just read (get/list) or just write operations + +### Dynamic tools + +If you specify `--tools=dynamic` to the MCP server, instead of exposing one tool per endpoint in the API, it will +expose the following tools: + +1. `list_api_endpoints` - Discovers available endpoints, with optional filtering by search query +2. `get_api_endpoint_schema` - Gets detailed schema information for a specific endpoint +3. `invoke_api_endpoint` - Executes any endpoint with the appropriate parameters + +This allows you to have the full set of API endpoints available to your MCP Client, while not requiring that all +of their schemas be loaded into context at once. Instead, the LLM will automatically use these tools together to +search for, look up, and invoke endpoints dynamically. However, due to the indirect nature of the schemas, it +can struggle to provide the correct properties a bit more than when tools are imported explicitly. Therefore, +you can opt-in to explicit tools, the dynamic tools, or both. + +See more information with `--help`. + +All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`). + +Use `--list` to see the list of available tools, or see below. + +### Specifying the MCP Client + +Different clients have varying abilities to handle arbitrary tools and schemas. + +You can specify the client you are using with the `--client` argument, and the MCP server will automatically +serve tools and schemas that are more compatible with that client. + +- `--client=`: Set all capabilities based on a known MCP client + + - Valid values: `openai-agents`, `claude`, `claude-code`, `cursor` + - Example: `--client=cursor` + +Additionally, if you have a client not on the above list, or the client has gotten better +over time, you can manually enable or disable certain capabilities: + +- `--capability=`: Specify individual client capabilities + - Available capabilities: + - `top-level-unions`: Enable support for top-level unions in tool schemas + - `valid-json`: Enable JSON string parsing for arguments + - `refs`: Enable support for $ref pointers in schemas + - `unions`: Enable support for union types (anyOf) in schemas + - `formats`: Enable support for format validations in schemas (e.g. date-time, email) + - `tool-name-length=N`: Set maximum tool name length to N characters + - Example: `--capability=top-level-unions --capability=tool-name-length=40` + - Example: `--capability=top-level-unions,tool-name-length=40` + +### Examples + +1. Filter for read operations on cards: + +```bash +--resource=cards --operation=read +``` + +2. Exclude specific tools while including others: + +```bash +--resource=cards --no-tool=create_cards +``` + +3. Configure for Cursor client with custom max tool name length: + +```bash +--client=cursor --capability=tool-name-length=40 +``` + +4. Complex filtering with multiple criteria: + +```bash +--resource=cards,accounts --operation=read --tag=kyc --no-tool=create_cards +``` + +## Running remotely + +Launching the client with `--transport=http` launches the server as a remote server using Streamable HTTP transport. The `--port` setting can choose the port it will run on, and the `--socket` setting allows it to run on a Unix socket. + +Authorization can be provided via the `Authorization` header using the Basic scheme. + +Additionally, authorization can be provided via the following headers: +| Header | Equivalent client option | Security scheme | +| ---------------------------------- | ------------------------ | --------------- | +| `x-imagekit-private-key` | `privateKey` | basicAuth | +| `x-optional-imagekit-ignores-this` | `password` | basicAuth | + +A configuration JSON for this server might look like this, assuming the server is hosted at `http://localhost:3000`: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "url": "http://localhost:3000", + "headers": { + "Authorization": "Basic " + } + } + } +} +``` + +The command-line arguments for filtering tools and specifying clients can also be used as query parameters in the URL. +For example, to exclude specific tools while including others, use the URL: + +``` +http://localhost:3000?resource=cards&resource=accounts&no_tool=create_cards +``` + +Or, to configure for the Cursor client, with a custom max tool name length, use the URL: + +``` +http://localhost:3000?client=cursor&capability=tool-name-length%3D40 +``` + +## Importing the tools and server individually + +```js +// Import the server, generated endpoints, or the init function +import { server, endpoints, init } from "imagekit-api-mcp/server"; + +// import a specific tool +import createCustomMetadataFields from "imagekit-api-mcp/tools/custom-metadata-fields/create-custom-metadata-fields"; + +// initialize the server and all endpoints +init({ server, endpoints }); + +// manually start server +const transport = new StdioServerTransport(); +await server.connect(transport); + +// or initialize your own server with specific tools +const myServer = new McpServer(...); + +// define your own endpoint +const myCustomEndpoint = { + tool: { + name: 'my_custom_tool', + description: 'My custom tool', + inputSchema: zodToJsonSchema(z.object({ a_property: z.string() })), + }, + handler: async (client: client, args: any) => { + return { myResponse: 'Hello world!' }; + }) +}; + +// initialize the server with your custom endpoints +init({ server: myServer, endpoints: [createCustomMetadataFields, myCustomEndpoint] }); +``` + +## Available Tools + +The following tools are available in this MCP server. + +### Resource `customMetadataFields`: + +- `create_custom_metadata_fields` (`write`): This API creates a new custom metadata field. Once a custom metadata field is created either through this API or using the dashboard UI, its value can be set on the assets. The value of a field for an asset can be set using the media library UI or programmatically through upload or update assets API. +- `update_custom_metadata_fields` (`write`): This API updates the label or schema of an existing custom metadata field. +- `list_custom_metadata_fields` (`read`): This API returns the array of created custom metadata field objects. By default the API returns only non deleted field objects, but you can include deleted fields in the API response. +- `delete_custom_metadata_fields` (`write`): This API deletes a custom metadata field. Even after deleting a custom metadata field, you cannot create any new custom metadata field with the same name. + +### Resource `files`: + +- `update_files` (`write`): This API updates the details or attributes of the current version of the file. You can update `tags`, `customCoordinates`, `customMetadata`, publication status, remove existing `AITags` and apply extensions using this API. +- `delete_files` (`write`): This API deletes the file and all its file versions permanently. + + Note: If a file or specific transformation has been requested in the past, then the response is cached. Deleting a file does not purge the cache. You can purge the cache using purge cache API. + +- `copy_files` (`write`): This will copy a file from one folder to another. + + Note: If any file at the destination has the same name as the source file, then the source file and its versions (if `includeFileVersions` is set to true) will be appended to the destination file version history. + +- `get_files` (`read`): This API returns an object with details or attributes about the current version of the file. +- `move_files` (`write`): This will move a file and all its versions from one folder to another. + + Note: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file. + +- `rename_files` (`write`): You can rename an already existing file in the media library using rename file API. This operation would rename all file versions of the file. + + Note: The old URLs will stop working. The file/file version URLs cached on CDN will continue to work unless a purge is requested. + +- `upload_files` (`write`): ImageKit.io allows you to upload files directly from both the server and client sides. For server-side uploads, private API key authentication is used. For client-side uploads, generate a one-time `token`, `signature`, and `expire` from your secure backend using private API. [Learn more](/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload) about how to implement client-side file upload. + + The [V2 API](/docs/api-reference/upload-file/upload-file-v2) enhances security by verifying the entire payload using JWT. + + **File size limit** \ + On the free plan, the maximum upload file sizes are 20MB for images, audio, and raw files and 100MB for videos. On the paid plan, these limits increase to 40MB for images, audio, and raw files and 2GB for videos. These limits can be further increased with higher-tier plans. + + **Version limit** \ + A file can have a maximum of 100 versions. + + **Demo applications** + + - A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, and more. + - [Quick start guides](/docs/quick-start-guides) for various frameworks and technologies. + +### Resource `files.bulk`: + +- `delete_files_bulk` (`write`): This API deletes multiple files and all their file versions permanently. + + Note: If a file or specific transformation has been requested in the past, then the response is cached. Deleting a file does not purge the cache. You can purge the cache using purge cache API. + + A maximum of 100 files can be deleted at a time. + +- `add_tags_files_bulk` (`write`): This API adds tags to multiple files in bulk. A maximum of 50 files can be specified at a time. +- `remove_ai_tags_files_bulk` (`write`): This API removes AITags from multiple files in bulk. A maximum of 50 files can be specified at a time. +- `remove_tags_files_bulk` (`write`): This API removes tags from multiple files in bulk. A maximum of 50 files can be specified at a time. + +### Resource `files.versions`: + +- `list_files_versions` (`read`): This API returns details of all versions of a file. +- `delete_files_versions` (`write`): This API deletes a non-current file version permanently. The API returns an empty response. + + Note: If you want to delete all versions of a file, use the delete file API. + +- `get_files_versions` (`read`): This API returns an object with details or attributes of a file version. +- `restore_files_versions` (`write`): This API restores a file version as the current file version. + +### Resource `files.metadata`: + +- `get_files_metadata` (`read`): You can programmatically get image EXIF, pHash, and other metadata for uploaded files in the ImageKit.io media library using this API. + + You can also get the metadata in upload API response by passing `metadata` in `responseFields` parameter. + +- `get_from_url_files_metadata` (`read`): Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL using this API. + +### Resource `assets`: + +- `list_assets` (`read`): This API can list all the uploaded files and folders in your ImageKit.io media library. In addition, you can fine-tune your query by specifying various filters by generating a query string in a Lucene-like syntax and provide this generated string as the value of the `searchQuery`. + +### Resource `cache.invalidation`: + +- `create_cache_invalidation` (`write`): This API will purge CDN cache and ImageKit.io's internal cache for a file. Note: Purge cache is an asynchronous process and it may take some time to reflect the changes. +- `get_cache_invalidation` (`read`): This API returns the status of a purge cache request. + +### Resource `folders`: + +- `create_folders` (`write`): This will create a new folder. You can specify the folder name and location of the parent folder where this new folder should be created. +- `delete_folders` (`write`): This will delete a folder and all its contents permanently. The API returns an empty response. +- `copy_folders` (`write`): This will copy one folder into another. The selected folder, its nested folders, files, and their versions (in `includeVersions` is set to true) are copied in this operation. Note: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file version history. +- `move_folders` (`write`): This will move one folder into another. The selected folder, its nested folders, files, and their versions are moved in this operation. Note: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file version history. +- `rename_folders` (`write`): This API allows you to rename an existing folder. The folder and all its nested assets and sub-folders will remain unchanged, but their paths will be updated to reflect the new folder name. + +### Resource `folders.job`: + +- `get_folders_job` (`read`): This API returns the status of a bulk job like copy and move folder operations. + +### Resource `accounts.usage`: + +- `get_accounts_usage` (`read`): Get the account usage information between two dates. Note that the API response includes data from the start date while excluding data from the end date. In other words, the data covers the period starting from the specified start date up to, but not including, the end date. + +### Resource `accounts.origins`: + +- `create_accounts_origins` (`write`): **Note:** This API is currently in beta. + Creates a new origin and returns the origin object. +- `update_accounts_origins` (`write`): **Note:** This API is currently in beta. + Updates the origin identified by `id` and returns the updated origin object. +- `list_accounts_origins` (`read`): **Note:** This API is currently in beta. + Returns an array of all configured origins for the current account. +- `delete_accounts_origins` (`write`): **Note:** This API is currently in beta. + Permanently removes the origin identified by `id`. If the origin is in use by any URL‑endpoints, the API will return an error. +- `get_accounts_origins` (`read`): **Note:** This API is currently in beta. + Retrieves the origin identified by `id`. + +### Resource `accounts.urlEndpoints`: + +- `create_accounts_url_endpoints` (`write`): **Note:** This API is currently in beta. + Creates a new URL‑endpoint and returns the resulting object. +- `update_accounts_url_endpoints` (`write`): **Note:** This API is currently in beta. + Updates the URL‑endpoint identified by `id` and returns the updated object. +- `list_accounts_url_endpoints` (`read`): **Note:** This API is currently in beta. + Returns an array of all URL‑endpoints configured including the default URL-endpoint generated by ImageKit during account creation. +- `delete_accounts_url_endpoints` (`write`): **Note:** This API is currently in beta. + Deletes the URL‑endpoint identified by `id`. You cannot delete the default URL‑endpoint created by ImageKit during account creation. +- `get_accounts_url_endpoints` (`read`): **Note:** This API is currently in beta. + Retrieves the URL‑endpoint identified by `id`. + +### Resource `beta.v2.files`: + +- `upload_v2_beta_files` (`write`): The V2 API enhances security by verifying the entire payload using JWT. This API is in beta. + + ImageKit.io allows you to upload files directly from both the server and client sides. For server-side uploads, private API key authentication is used. For client-side uploads, generate a one-time `token` from your secure backend using private API. [Learn more](/docs/api-reference/upload-file/upload-file-v2#how-to-implement-secure-client-side-file-upload) about how to implement secure client-side file upload. + + **File size limit** \ + On the free plan, the maximum upload file sizes are 20MB for images, audio, and raw files, and 100MB for videos. On the paid plan, these limits increase to 40MB for images, audio, and raw files, and 2GB for videos. These limits can be further increased with higher-tier plans. + + **Version limit** \ + A file can have a maximum of 100 versions. + + **Demo applications** + + - A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, and more. + - [Quick start guides](/docs/quick-start-guides) for various frameworks and technologies. diff --git a/packages/mcp-server/build b/packages/mcp-server/build new file mode 100644 index 00000000..4c6474cc --- /dev/null +++ b/packages/mcp-server/build @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -exuo pipefail + +rm -rf dist; mkdir dist + +# Copy src to dist/src and build from dist/src into dist, so that +# the source map for index.js.map will refer to ./src/index.ts etc +cp -rp src README.md dist + +for file in LICENSE; do + if [ -e "../../${file}" ]; then cp "../../${file}" dist; fi +done + +for file in CHANGELOG.md; do + if [ -e "${file}" ]; then cp "${file}" dist; fi +done + +# this converts the export map paths for the dist directory +# and does a few other minor things +PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist/package.json + +# updates the `@imagekit/nodejs` dependency to point to NPM +node scripts/postprocess-dist-package-json.cjs + +# build to .js/.mjs/.d.ts files +./node_modules/.bin/tsc-multi + +cp tsconfig.dist-src.json dist/src/tsconfig.json + +chmod +x dist/index.js + +DIST_PATH=./dist PKG_IMPORT_PATH=imagekit-api-mcp/ node ../../scripts/utils/postprocess-files.cjs + +# mcp bundle +rm -rf dist-bundle imagekit_nodejs_api.mcpb; mkdir dist-bundle + +# copy package.json +PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist-bundle/package.json + +# copy files +node scripts/copy-bundle-files.cjs + +# install runtime deps +cd dist-bundle +npm install +cd .. + +# pack bundle +cp manifest.json dist-bundle + +npx mcpb pack dist-bundle imagekit_nodejs_api.mcpb + +npx mcpb sign imagekit_nodejs_api.mcpb --self-signed + +# clean up +rm -rf dist-bundle diff --git a/packages/mcp-server/cloudflare-worker/.gitignore b/packages/mcp-server/cloudflare-worker/.gitignore new file mode 100644 index 00000000..5addd9af --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/.gitignore @@ -0,0 +1,178 @@ +node_modules + +.nx +.idea +.vscode +.zed +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* + +# wrangler project + +.dev.vars +.wrangler/ diff --git a/packages/mcp-server/cloudflare-worker/README.md b/packages/mcp-server/cloudflare-worker/README.md new file mode 100644 index 00000000..fc89a91d --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/README.md @@ -0,0 +1,100 @@ +# Remote MCP Server on Cloudflare with Stainless + +Remote MCP servers require OAuth, so this flow implements a local version of the OAuth redirects, but instead accepts the +API token and any other client configuration options that you'd need to instantiate your TypeScript client. + +## Usage + +The recommended way to use this project is to use the below "deploy to cloudflare" button to use this repo as a template for generating a server. + +[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/imagekit-developer/imagekit-nodejs/tree/main/packages/mcp-server/cloudflare-worker) + +## Develop locally + +```bash +# install dependencies +npm install + +# run locally +npm run dev +``` + +You should be able to open [`http://localhost:8787/`](http://localhost:8787/) in your browser + +## Connect the MCP inspector to your server + +To explore your new MCP api, you can use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector). + +- Start it with `npx @modelcontextprotocol/inspector` +- [Within the inspector](http://localhost:5173), switch the Transport Type to `SSE` and enter `http://localhost:8787/sse` as the URL of the MCP server to connect to, and click "Connect" +- You will navigate to a (mock) user/password login screen. Input any email and pass to login. +- You should be redirected back to the MCP Inspector and you can now list and call any defined tools! + +## Connect Claude Desktop to your local MCP server + +The MCP inspector is great, but we really want to connect this to Claude! Follow [Anthropic's Quickstart](https://modelcontextprotocol.io/quickstart/user) and within Claude Desktop go to Settings > Developer > Edit Config to find your configuration file. + +Open the file in your text editor and replace it with this configuration: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "npx", + "args": ["mcp-remote", "http://localhost:8787/sse"] + } + } +} +``` + +This will run a local proxy and let Claude talk to your MCP server over HTTP + +When you open Claude a browser window should open and allow you to login. You should see the tools available in the bottom right. Given the right prompt Claude should ask to call the tool. + +## Deploy to Cloudflare + +If you want to manually deploy this server (e.g. without the "deploy to cloudflare" button) + +1. `npx wrangler@latest kv namespace create remote-mcp-server-oauth-kv` +2. Follow the guidance to add the kv namespace ID to `wrangler.jsonc` +3. `npm run deploy` + +## Call your newly deployed remote MCP server from a remote MCP client + +Just like you did above in "Develop locally", run the MCP inspector: + +`npx @modelcontextprotocol/inspector@latest` + +Then enter the `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) of your Worker in the inspector as the URL of the MCP server to connect to, and click "Connect". + +You've now connected to your MCP server from a remote MCP client. + +## Connect Claude Desktop to your remote MCP server + +Update the Claude configuration file to point to your `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) and restart Claude + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "npx", + "args": ["mcp-remote", "https://worker-name.account-name.workers.dev/sse"] + } + } +} +``` + +## Debugging + +Should anything go wrong it can be helpful to restart Claude, or to try connecting directly to your +MCP server on the command line with the following command. + +```bash +npx mcp-remote http://localhost:8787/sse +``` + +In some rare cases it may help to clear the files added to `~/.mcp-auth` + +```bash +rm -rf ~/.mcp-auth +``` diff --git a/packages/mcp-server/cloudflare-worker/biome.json b/packages/mcp-server/cloudflare-worker/biome.json new file mode 100644 index 00000000..13b23ad3 --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.6.2/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignore": ["worker-configuration.d.ts"] + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noDebugger": "off", + "noConsoleLog": "off", + "noConfusingVoidType": "off" + }, + "style": { + "noNonNullAssertion": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentWidth": 4, + "lineWidth": 100 + } +} diff --git a/packages/mcp-server/cloudflare-worker/package.json b/packages/mcp-server/cloudflare-worker/package.json new file mode 100644 index 00000000..ee97af9e --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/package.json @@ -0,0 +1,27 @@ +{ + "name": "remote-mcp-server-with-stainless", + "version": "0.0.0", + "private": true, + "scripts": { + "deploy": "wrangler deploy", + "dev": "wrangler dev", + "format": "biome format --write", + "lint:fix": "biome lint --fix", + "start": "wrangler dev", + "cf-typegen": "wrangler types" + }, + "devDependencies": { + "marked": "^15.0.11", + "typescript": "^5.8.3", + "workers-mcp": "0.1.0-3", + "wrangler": "^4.15.2" + }, + "dependencies": { + "@cloudflare/workers-oauth-provider": "^0.0.5", + "@modelcontextprotocol/sdk": "^1.11.4", + "agents": "^0.0.88", + "hono": "^4.7.9", + "imagekit-api-mcp": "latest", + "zod": "^3.24.4" + } +} diff --git a/packages/mcp-server/cloudflare-worker/src/app.ts b/packages/mcp-server/cloudflare-worker/src/app.ts new file mode 100644 index 00000000..227b4523 --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/src/app.ts @@ -0,0 +1,106 @@ +import { Hono } from 'hono'; +import { + layout, + homeContent, + parseApproveFormBody, + renderAuthorizationApprovedContent, + renderLoggedOutAuthorizeScreen, + renderAuthorizationRejectedContent, +} from './utils'; +import type { OAuthHelpers } from '@cloudflare/workers-oauth-provider'; +import { McpOptions } from 'imagekit-api-mcp/server'; +import { ServerConfig } from '.'; + +export type Bindings = Env & { + OAUTH_PROVIDER: OAuthHelpers; +}; + +export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial) { + const app = new Hono<{ + Bindings: Bindings; + }>(); + + // Render a reasonable home page just to show the app is up + app.get('/', async (c) => { + const content = await homeContent(c.req.raw); + return c.html(layout(content, 'Home', config)); + }); + + // The /authorize page has a form that will POST to /approve + app.get('/authorize', async (c) => { + const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw); + + const content = await renderLoggedOutAuthorizeScreen(config, oauthReqInfo, defaultOptions); + return c.html(layout(content, 'Authorization', config)); + }); + + // This endpoint is responsible for validating any login information and + // then completing the authorization request with the OAUTH_PROVIDER + app.post('/approve', async (c) => { + const { action, oauthReqInfo, clientProps, clientConfig } = await parseApproveFormBody( + await c.req.parseBody(), + config, + ); + + if (action !== 'login_approve') { + return c.html( + layout( + await renderAuthorizationRejectedContent(oauthReqInfo?.redirectUri || ''), + 'Authorization Status', + config, + ), + ); + } + + if (!oauthReqInfo || !clientProps || !clientConfig) { + return c.html('INVALID LOGIN', 401); + } + + // We don't have a real user ID, just tokens, so we generate a random one + // Make this some stable ID if you want to look up the user's grants later. + const generatedUserId = crypto.randomUUID(); + + const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ + request: oauthReqInfo, + userId: generatedUserId, + metadata: {}, + scope: oauthReqInfo.scope, + props: { + clientProps, + clientConfig, + }, + }); + + return c.html( + layout(await renderAuthorizationApprovedContent(redirectTo), 'Authorization Status', config), + ); + }); + + // Render the authorize screen for demoing the OAuth flow (it won't actually log in) + app.get('/demo', async (c) => { + const content = await renderLoggedOutAuthorizeScreen(config, {} as any, defaultOptions); + return c.html(layout(content, 'Authorization', config)); + }); + + // Add a resource server .well-known to point clients to the correct auth server + const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, OPTIONS', + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Max-Age': '86400', + }; + app.options('/.well-known/oauth-protected-resource', async (c) => { + Object.entries(corsHeaders).forEach(([key, value]) => c.header(key, value)); + return c.body(null, 204); + }); + app.get('/.well-known/oauth-protected-resource', async (c) => { + Object.entries(corsHeaders).forEach(([key, value]) => c.header(key, value)); + const baseURL = new URL('/', c.req.url).toString(); + return c.json({ + resource: baseURL, + authorization_servers: [baseURL], + }); + }); + + return app; +} diff --git a/packages/mcp-server/cloudflare-worker/src/index.ts b/packages/mcp-server/cloudflare-worker/src/index.ts new file mode 100644 index 00000000..75fcea3c --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/src/index.ts @@ -0,0 +1,109 @@ +import { makeOAuthConsent } from './app'; +import { McpAgent } from 'agents/mcp'; +import OAuthProvider from '@cloudflare/workers-oauth-provider'; +import { McpOptions, initMcpServer, server, ClientOptions } from 'imagekit-api-mcp/server'; + +type MCPProps = { + clientProps: ClientOptions; + clientConfig: McpOptions; +}; + +/** + * The information displayed on the OAuth consent screen + */ +const serverConfig: ServerConfig = { + orgName: 'ImageKit', + instructionsUrl: undefined, // Set a url for where you show users how to get an API key + logoUrl: undefined, // Set a custom logo url to appear during the OAuth flow + clientProperties: [ + { + key: 'privateKey', + label: 'Private Key', + description: + 'Your ImageKit private API key (starts with `private_`).\nYou can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys).\n', + required: true, + default: undefined, + placeholder: 'My Private Key', + type: 'password', + }, + { + key: 'password', + label: 'Password', + description: + 'ImageKit uses your API key as username and ignores the password. \nThe SDK sets a dummy value. You can ignore this field.\n', + required: false, + default: 'do_not_set', + placeholder: 'My Password', + type: 'password', + }, + { + key: 'webhookSecret', + label: 'Webhook Secret', + description: + "Your ImageKit webhook secret for verifying webhook signatures (starts with `whsec_`).\nYou can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks).\nOnly required if you're using webhooks.\n", + required: false, + default: null, + placeholder: 'My Webhook Secret', + type: 'string', + }, + ], +}; + +export class MyMCP extends McpAgent { + server = server; + + async init() { + initMcpServer({ + server: this.server, + clientOptions: this.props.clientProps, + mcpOptions: this.props.clientConfig, + }); + } +} + +export type ServerConfig = { + /** + * The name of the company/project + */ + orgName: string; + + /** + * An optional company logo image + */ + logoUrl?: string; + + /** + * An optional URL with instructions for users to get an API key + */ + instructionsUrl?: string; + + /** + * Properties collected to initialize the client + */ + clientProperties: ClientProperty[]; +}; + +export type ClientProperty = { + key: string; + label: string; + description?: string; + required: boolean; + default?: unknown; + placeholder?: string; + type: 'string' | 'number' | 'password' | 'select'; + options?: { label: string; value: string }[]; +}; + +// Export the OAuth handler as the default +export default new OAuthProvider({ + apiHandlers: { + // @ts-expect-error + '/sse': MyMCP.serveSSE('/sse'), // legacy SSE + // @ts-expect-error + '/mcp': MyMCP.serve('/mcp'), // Streaming HTTP + }, + defaultHandler: makeOAuthConsent(serverConfig), + authorizeEndpoint: '/authorize', + tokenEndpoint: '/token', + clientRegistrationEndpoint: '/register', +}); diff --git a/packages/mcp-server/cloudflare-worker/src/utils.ts b/packages/mcp-server/cloudflare-worker/src/utils.ts new file mode 100644 index 00000000..7076424d --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/src/utils.ts @@ -0,0 +1,480 @@ +// Helper to generate the layout +import { html, raw } from 'hono/html'; +import type { HtmlEscapedString } from 'hono/utils/html'; +import { marked } from 'marked'; +import type { AuthRequest } from '@cloudflare/workers-oauth-provider'; +import { env } from 'cloudflare:workers'; +import { ServerConfig, McpOptions, ClientType, Filter, ClientProperty } from 'imagekit-api-mcp/server'; + +export const layout = (content: HtmlEscapedString | string, title: string, config: ServerConfig) => html` + + + + + + ${title} - ${config.orgName} MCP server + + + + + +
${content}
+
+
+

© ${new Date().getFullYear()} ${config.orgName}. All rights reserved.

+
+
+ + +`; + +export const homeContent = async (req: Request): Promise => { + // We have the README symlinked into the static directory, so we can fetch it + // and render it into HTML + const origin = new URL(req.url).origin; + const res = await env.ASSETS.fetch(`${origin}/home.md`); + let markdown = await res.text(); + markdown = markdown.replaceAll('{{cloudflareWorkerUrl}}', origin + '/sse'); + const content = await marked(markdown); + return html`
${raw(content)}
`; +}; + +export const renderLoggedOutAuthorizeScreen = async ( + config: ServerConfig, + oauthReqInfo: AuthRequest, + defaultOptions?: Partial, +) => { + const checked = (condition: boolean) => (condition ? 'checked' : ''); + const selected = (condition: boolean) => (condition ? 'selected' : ''); + + // Helper to check if a capability is enabled by default + const hasCapability = (capability: string) => { + if (!defaultOptions?.capabilities) { + // Default capabilities when none specified + return ['refs', 'unions', 'formats'].includes(capability); + } + switch (capability) { + case 'top-level-unions': + return defaultOptions.capabilities.topLevelUnions || false; + case 'valid-json': + return defaultOptions.capabilities.validJson || false; + case 'refs': + return defaultOptions.capabilities.refs || false; + case 'unions': + return defaultOptions.capabilities.unions || false; + case 'formats': + return defaultOptions.capabilities.formats || false; + default: + return false; + } + }; + + // Helper to check if an operation is enabled by default + const hasOperation = (operation: string) => { + if (!defaultOptions?.filters) { + // Default operations when none specified + return ['read', 'write'].includes(operation); + } + return defaultOptions.filters.some( + (f) => f.type === 'operation' && f.op === 'include' && f.value === operation, + ); + }; + const renderField = (field: ClientProperty) => { + if (field.type === 'select' && field.options) { + return html` +
+ + +
+ `; + } + return html` +
+ + +
+ `; + }; + + return html` +
+ ${config.logoUrl ? html`` : ''} + +

+ Authorizing ${config.orgName} MCP server +

+ +
+

+ Enter your credentials to initialize the connection with your MCP client. +

+ If you're not sure how to configure your client, see the + ${config.instructionsUrl ? + html`instructions` + : 'instructions'} + to get started. +
+
+ +
${config.clientProperties.map(renderField)}
+ +
+
+ + Configuration Options + +
+
+ + +
+ +
+ + +
+ + ? + +
+ Have the LLM dynamically discover the endpoints, instead of directly exposing one tool per + endpoint. +
+
+
+ +
+
+ + +
+ + ? + +
+ Restrict the available tools to only be able to read data. +
+
+
+
+
+
+
+ + + +
+
+ `; +}; + +export const renderApproveContent = async (message: string, status: string, redirectUrl: string) => { + return html` +
+
+ + ${status === 'success' ? '✓' : '✗'} + +
+

${message}

+

You will be redirected back to the application shortly.

+ + Return to Home + + ${raw(` + + `)} +
+ `; +}; + +export const renderAuthorizationApprovedContent = async (redirectUrl: string) => { + return renderApproveContent('Authorization approved!', 'success', redirectUrl); +}; + +export const renderAuthorizationRejectedContent = async (redirectUrl: string) => { + return renderApproveContent('Authorization rejected.', 'error', redirectUrl); +}; + +export const parseApproveFormBody = async ( + body: { + [x: string]: string | File; + }, + config: ServerConfig, +) => { + const parsedClientProps = Object.fromEntries( + config.clientProperties.map((prop: ClientProperty) => { + const rawValue = body[`clientopt_${prop.key}`]; + const value = prop.type === 'number' ? Number(rawValue) : rawValue; + return [prop.key, value]; + }), + ); + + const filters: Filter[] = []; + + if (body.read_only_operations === 'on') { + filters.push({ + type: 'operation', + op: 'exclude', + value: 'write', + }); + } + + // Parse advanced options + const clientConfig: McpOptions = { + client: (body.client as ClientType) || undefined, + includeDynamicTools: body.dynamic_tools === 'on', + includeAllTools: body.dynamic_tools !== 'on', + filters, + }; + + let oauthReqInfo: AuthRequest | null = null; + try { + oauthReqInfo = JSON.parse(body.oauthReqInfo as string) as AuthRequest; + if (Object.keys(oauthReqInfo).length === 0) { + oauthReqInfo = null; + } + } catch (e) { + oauthReqInfo = null; + } + + return { oauthReqInfo, clientProps: parsedClientProps, clientConfig, action: body.action }; +}; diff --git a/packages/mcp-server/cloudflare-worker/static/home.md b/packages/mcp-server/cloudflare-worker/static/home.md new file mode 100644 index 00000000..f40a97d0 --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/static/home.md @@ -0,0 +1,86 @@ +# ImageKit Remote MCP Server + +This MCP server is available at: + +``` +{{cloudflareWorkerUrl}} +``` + +### Claude.ai + +To connect Claude Web to this MCP server: + +1. Open Claude Web +2. Go to Settings -> Connectors +3. Click "+ Add Custom Connector" +4. Add the MCP server URL: `{{cloudflareWorkerUrl}}` + +### Claude Desktop + +Claude Desktop requires using the `mcp-remote` package to connect to remote MCP servers: + +1. Edit your Claude Desktop configuration file: + - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` + - Windows: `%APPDATA%\Claude\claude_desktop_config.json` +2. Add the following configuration: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "npx", + "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"] + } + } +} +``` + +3. Restart Claude Desktop to see the MCP server connection (look for the hammer icon) + +### Cursor + +1. Edit your Cursor MCP configuration file at `~/.cursor/mcp.json` +2. Add the following configuration: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "npx", + "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"] + } + } +} +``` + +### Windsurf + +1. Edit your Windsurf configuration file at `~/.codeium/windsurf/mcp_config.json` +2. Add the following configuration: + +```json +{ + "mcpServers": { + "imagekit_nodejs_api": { + "command": "npx", + "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"] + } + } +} +``` + +## Troubleshooting + +If you encounter issues connecting: + +1. Ensure you have Node.js 18 or higher installed +2. Try clearing MCP authentication cache: `rm -rf ~/.mcp-auth` +3. Restart your MCP client application +4. Check client logs for error messages + +## Learn More + +For more information about MCP: + +- [MCP Introduction](https://modelcontextprotocol.io/introduction) +- [mcp-remote package](https://www.npmjs.com/package/mcp-remote) diff --git a/packages/mcp-server/cloudflare-worker/tsconfig.json b/packages/mcp-server/cloudflare-worker/tsconfig.json new file mode 100644 index 00000000..3464dc29 --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2021", + "lib": ["es2021"], + "jsx": "react-jsx", + "module": "es2022", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "allowJs": true, + "checkJs": false, + "noEmit": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["worker-configuration.d.ts", "src/**/*.ts"] +} diff --git a/packages/mcp-server/cloudflare-worker/worker-configuration.d.ts b/packages/mcp-server/cloudflare-worker/worker-configuration.d.ts new file mode 100644 index 00000000..813a10ff --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/worker-configuration.d.ts @@ -0,0 +1,5707 @@ +// Generated by Wrangler by running `wrangler types` (hash: 9c6c6f60ec3f74c7ee99dddce04c3506) +// Runtime types generated with workerd@1.20250317.0 2025-03-10 +declare namespace Cloudflare { + interface Env { + OAUTH_KV: KVNamespace; + MCP_OBJECT: DurableObjectNamespace; + ASSETS: Fetcher; + } +} +interface Env extends Cloudflare.Env {} + +// Begin runtime types +/*! ***************************************************************************** +Copyright (c) Cloudflare. All rights reserved. +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +declare var onmessage: never; +/** + * An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException) + */ +declare class DOMException extends Error { + constructor(message?: string, name?: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */ + readonly message: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ + readonly name: string; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code) + */ + readonly code: number; + static readonly INDEX_SIZE_ERR: number; + static readonly DOMSTRING_SIZE_ERR: number; + static readonly HIERARCHY_REQUEST_ERR: number; + static readonly WRONG_DOCUMENT_ERR: number; + static readonly INVALID_CHARACTER_ERR: number; + static readonly NO_DATA_ALLOWED_ERR: number; + static readonly NO_MODIFICATION_ALLOWED_ERR: number; + static readonly NOT_FOUND_ERR: number; + static readonly NOT_SUPPORTED_ERR: number; + static readonly INUSE_ATTRIBUTE_ERR: number; + static readonly INVALID_STATE_ERR: number; + static readonly SYNTAX_ERR: number; + static readonly INVALID_MODIFICATION_ERR: number; + static readonly NAMESPACE_ERR: number; + static readonly INVALID_ACCESS_ERR: number; + static readonly VALIDATION_ERR: number; + static readonly TYPE_MISMATCH_ERR: number; + static readonly SECURITY_ERR: number; + static readonly NETWORK_ERR: number; + static readonly ABORT_ERR: number; + static readonly URL_MISMATCH_ERR: number; + static readonly QUOTA_EXCEEDED_ERR: number; + static readonly TIMEOUT_ERR: number; + static readonly INVALID_NODE_TYPE_ERR: number; + static readonly DATA_CLONE_ERR: number; + get stack(): any; + set stack(value: any); +} +type WorkerGlobalScopeEventMap = { + fetch: FetchEvent; + scheduled: ScheduledEvent; + queue: QueueEvent; + unhandledrejection: PromiseRejectionEvent; + rejectionhandled: PromiseRejectionEvent; +}; +declare abstract class WorkerGlobalScope extends EventTarget { + EventTarget: typeof EventTarget; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */ +interface Console { + "assert"(condition?: boolean, ...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static) */ + clear(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static) */ + count(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countreset_static) */ + countReset(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static) */ + debug(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static) */ + dir(item?: any, options?: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static) */ + dirxml(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static) */ + error(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static) */ + group(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupcollapsed_static) */ + groupCollapsed(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupend_static) */ + groupEnd(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static) */ + info(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) */ + log(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static) */ + table(tabularData?: any, properties?: string[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static) */ + time(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeend_static) */ + timeEnd(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timelog_static) */ + timeLog(label?: string, ...data: any[]): void; + timeStamp(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static) */ + trace(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static) */ + warn(...data: any[]): void; +} +declare const console: Console; +type BufferSource = ArrayBufferView | ArrayBuffer; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array; +declare namespace WebAssembly { + class CompileError extends Error { + constructor(message?: string); + } + class RuntimeError extends Error { + constructor(message?: string); + } + type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; + interface GlobalDescriptor { + value: ValueType; + mutable?: boolean; + } + class Global { + constructor(descriptor: GlobalDescriptor, value?: any); + value: any; + valueOf(): any; + } + type ImportValue = ExportValue | number; + type ModuleImports = Record; + type Imports = Record; + type ExportValue = Function | Global | Memory | Table; + type Exports = Record; + class Instance { + constructor(module: Module, imports?: Imports); + readonly exports: Exports; + } + interface MemoryDescriptor { + initial: number; + maximum?: number; + shared?: boolean; + } + class Memory { + constructor(descriptor: MemoryDescriptor); + readonly buffer: ArrayBuffer; + grow(delta: number): number; + } + type ImportExportKind = "function" | "global" | "memory" | "table"; + interface ModuleExportDescriptor { + kind: ImportExportKind; + name: string; + } + interface ModuleImportDescriptor { + kind: ImportExportKind; + module: string; + name: string; + } + abstract class Module { + static customSections(module: Module, sectionName: string): ArrayBuffer[]; + static exports(module: Module): ModuleExportDescriptor[]; + static imports(module: Module): ModuleImportDescriptor[]; + } + type TableKind = "anyfunc" | "externref"; + interface TableDescriptor { + element: TableKind; + initial: number; + maximum?: number; + } + class Table { + constructor(descriptor: TableDescriptor, value?: any); + readonly length: number; + get(index: number): any; + grow(delta: number, value?: any): number; + set(index: number, value?: any): void; + } + function instantiate(module: Module, imports?: Imports): Promise; + function validate(bytes: BufferSource): boolean; +} +/** + * This ServiceWorker API interface represents the global execution context of a service worker. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope) + */ +interface ServiceWorkerGlobalScope extends WorkerGlobalScope { + DOMException: typeof DOMException; + WorkerGlobalScope: typeof WorkerGlobalScope; + btoa(data: string): string; + atob(data: string): string; + setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; + setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearTimeout(timeoutId: number | null): void; + setInterval(callback: (...args: any[]) => void, msDelay?: number): number; + setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearInterval(timeoutId: number | null): void; + queueMicrotask(task: Function): void; + structuredClone(value: T, options?: StructuredSerializeOptions): T; + reportError(error: any): void; + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + self: ServiceWorkerGlobalScope; + crypto: Crypto; + caches: CacheStorage; + scheduler: Scheduler; + performance: Performance; + Cloudflare: Cloudflare; + readonly origin: string; + Event: typeof Event; + ExtendableEvent: typeof ExtendableEvent; + CustomEvent: typeof CustomEvent; + PromiseRejectionEvent: typeof PromiseRejectionEvent; + FetchEvent: typeof FetchEvent; + TailEvent: typeof TailEvent; + TraceEvent: typeof TailEvent; + ScheduledEvent: typeof ScheduledEvent; + MessageEvent: typeof MessageEvent; + CloseEvent: typeof CloseEvent; + ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader; + ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader; + ReadableStream: typeof ReadableStream; + WritableStream: typeof WritableStream; + WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter; + TransformStream: typeof TransformStream; + ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy; + CountQueuingStrategy: typeof CountQueuingStrategy; + ErrorEvent: typeof ErrorEvent; + EventSource: typeof EventSource; + ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest; + ReadableStreamDefaultController: typeof ReadableStreamDefaultController; + ReadableByteStreamController: typeof ReadableByteStreamController; + WritableStreamDefaultController: typeof WritableStreamDefaultController; + TransformStreamDefaultController: typeof TransformStreamDefaultController; + CompressionStream: typeof CompressionStream; + DecompressionStream: typeof DecompressionStream; + TextEncoderStream: typeof TextEncoderStream; + TextDecoderStream: typeof TextDecoderStream; + Headers: typeof Headers; + Body: typeof Body; + Request: typeof Request; + Response: typeof Response; + WebSocket: typeof WebSocket; + WebSocketPair: typeof WebSocketPair; + WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair; + AbortController: typeof AbortController; + AbortSignal: typeof AbortSignal; + TextDecoder: typeof TextDecoder; + TextEncoder: typeof TextEncoder; + navigator: Navigator; + Navigator: typeof Navigator; + URL: typeof URL; + URLSearchParams: typeof URLSearchParams; + URLPattern: typeof URLPattern; + Blob: typeof Blob; + File: typeof File; + FormData: typeof FormData; + Crypto: typeof Crypto; + SubtleCrypto: typeof SubtleCrypto; + CryptoKey: typeof CryptoKey; + CacheStorage: typeof CacheStorage; + Cache: typeof Cache; + FixedLengthStream: typeof FixedLengthStream; + IdentityTransformStream: typeof IdentityTransformStream; + HTMLRewriter: typeof HTMLRewriter; + GPUAdapter: typeof GPUAdapter; + GPUOutOfMemoryError: typeof GPUOutOfMemoryError; + GPUValidationError: typeof GPUValidationError; + GPUInternalError: typeof GPUInternalError; + GPUDeviceLostInfo: typeof GPUDeviceLostInfo; + GPUBufferUsage: typeof GPUBufferUsage; + GPUShaderStage: typeof GPUShaderStage; + GPUMapMode: typeof GPUMapMode; + GPUTextureUsage: typeof GPUTextureUsage; + GPUColorWrite: typeof GPUColorWrite; +} +declare function addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; +declare function removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ +declare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */ +declare function btoa(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */ +declare function atob(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearTimeout) */ +declare function clearTimeout(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearInterval) */ +declare function clearInterval(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/queueMicrotask) */ +declare function queueMicrotask(task: Function): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/reportError) */ +declare function reportError(error: any): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch) */ +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +declare const self: ServiceWorkerGlobalScope; +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare const crypto: Crypto; +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare const caches: CacheStorage; +declare const scheduler: Scheduler; +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +declare const performance: Performance; +declare const Cloudflare: Cloudflare; +declare const origin: string; +declare const navigator: Navigator; +interface TestController { +} +interface ExecutionContext { + waitUntil(promise: Promise): void; + passThroughOnException(): void; + props: any; +} +type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise; +type ExportedHandlerTailHandler = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTraceHandler = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTailStreamHandler = (event: TailStream.TailEvent, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise; +type ExportedHandlerScheduledHandler = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerQueueHandler = (batch: MessageBatch, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTestHandler = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise; +interface ExportedHandler { + fetch?: ExportedHandlerFetchHandler; + tail?: ExportedHandlerTailHandler; + trace?: ExportedHandlerTraceHandler; + tailStream?: ExportedHandlerTailStreamHandler; + scheduled?: ExportedHandlerScheduledHandler; + test?: ExportedHandlerTestHandler; + email?: EmailExportedHandler; + queue?: ExportedHandlerQueueHandler; +} +interface StructuredSerializeOptions { + transfer?: any[]; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent) */ +declare abstract class PromiseRejectionEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise) */ + readonly promise: Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason) */ + readonly reason: any; +} +declare abstract class Navigator { + sendBeacon(url: string, body?: (ReadableStream | string | (ArrayBuffer | ArrayBufferView) | Blob | FormData | URLSearchParams | URLSearchParams)): boolean; + readonly userAgent: string; + readonly gpu?: GPU; +} +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +interface Performance { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */ + readonly timeOrigin: number; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */ + now(): number; +} +interface AlarmInvocationInfo { + readonly isRetry: boolean; + readonly retryCount: number; +} +interface Cloudflare { + readonly compatibilityFlags: Record; +} +interface DurableObject { + fetch(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; +} +type DurableObjectStub = Fetcher & { + readonly id: DurableObjectId; + readonly name?: string; +}; +interface DurableObjectId { + toString(): string; + equals(other: DurableObjectId): boolean; + readonly name?: string; +} +interface DurableObjectNamespace { + newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId; + idFromName(name: string): DurableObjectId; + idFromString(id: string): DurableObjectId; + get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub; + jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace; +} +type DurableObjectJurisdiction = "eu" | "fedramp"; +interface DurableObjectNamespaceNewUniqueIdOptions { + jurisdiction?: DurableObjectJurisdiction; +} +type DurableObjectLocationHint = "wnam" | "enam" | "sam" | "weur" | "eeur" | "apac" | "oc" | "afr" | "me"; +interface DurableObjectNamespaceGetDurableObjectOptions { + locationHint?: DurableObjectLocationHint; +} +interface DurableObjectState { + waitUntil(promise: Promise): void; + readonly id: DurableObjectId; + readonly storage: DurableObjectStorage; + container?: Container; + blockConcurrencyWhile(callback: () => Promise): Promise; + acceptWebSocket(ws: WebSocket, tags?: string[]): void; + getWebSockets(tag?: string): WebSocket[]; + setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void; + getWebSocketAutoResponse(): WebSocketRequestResponsePair | null; + getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null; + setHibernatableWebSocketEventTimeout(timeoutMs?: number): void; + getHibernatableWebSocketEventTimeout(): number | null; + getTags(ws: WebSocket): string[]; + abort(reason?: string): void; +} +interface DurableObjectTransaction { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + rollback(): void; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; +} +interface DurableObjectStorage { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + deleteAll(options?: DurableObjectPutOptions): Promise; + transaction(closure: (txn: DurableObjectTransaction) => Promise): Promise; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; + sync(): Promise; + sql: SqlStorage; + transactionSync(closure: () => T): T; + getCurrentBookmark(): Promise; + getBookmarkForTime(timestamp: number | Date): Promise; + onNextSessionRestoreBookmark(bookmark: string): Promise; +} +interface DurableObjectListOptions { + start?: string; + startAfter?: string; + end?: string; + prefix?: string; + reverse?: boolean; + limit?: number; + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetOptions { + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetAlarmOptions { + allowConcurrency?: boolean; +} +interface DurableObjectPutOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; + noCache?: boolean; +} +interface DurableObjectSetAlarmOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; +} +declare class WebSocketRequestResponsePair { + constructor(request: string, response: string); + get request(): string; + get response(): string; +} +interface AnalyticsEngineDataset { + writeDataPoint(event?: AnalyticsEngineDataPoint): void; +} +interface AnalyticsEngineDataPoint { + indexes?: ((ArrayBuffer | string) | null)[]; + doubles?: number[]; + blobs?: ((ArrayBuffer | string) | null)[]; +} +/** + * An event which takes place in the DOM. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event) + */ +declare class Event { + constructor(type: string, init?: EventInit); + /** + * Returns the type of event, e.g. "click", "hashchange", or "submit". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type) + */ + get type(): string; + /** + * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase) + */ + get eventPhase(): number; + /** + * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed) + */ + get composed(): boolean; + /** + * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles) + */ + get bubbles(): boolean; + /** + * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable) + */ + get cancelable(): boolean; + /** + * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented) + */ + get defaultPrevented(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue) + */ + get returnValue(): boolean; + /** + * Returns the object whose event listener's callback is currently being invoked. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) + */ + get currentTarget(): EventTarget | undefined; + /** + * Returns the object to which event is dispatched (its target). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) + */ + get target(): EventTarget | undefined; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement) + */ + get srcElement(): EventTarget | undefined; + /** + * Returns the event's timestamp as the number of milliseconds measured relative to the time origin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp) + */ + get timeStamp(): number; + /** + * Returns true if event was dispatched by the user agent, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted) + */ + get isTrusted(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + get cancelBubble(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + set cancelBubble(value: boolean); + /** + * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation) + */ + stopImmediatePropagation(): void; + /** + * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) + */ + preventDefault(): void; + /** + * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) + */ + stopPropagation(): void; + /** + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath) + */ + composedPath(): EventTarget[]; + static readonly NONE: number; + static readonly CAPTURING_PHASE: number; + static readonly AT_TARGET: number; + static readonly BUBBLING_PHASE: number; +} +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} +type EventListener = (event: EventType) => void; +interface EventListenerObject { + handleEvent(event: EventType): void; +} +type EventListenerOrEventListenerObject = EventListener | EventListenerObject; +/** + * EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget) + */ +declare class EventTarget = Record> { + constructor(); + /** + * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. + * + * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. + * + * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. + * + * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) + */ + addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; + /** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener) + */ + removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; + /** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ + dispatchEvent(event: EventMap[keyof EventMap]): boolean; +} +interface EventTargetEventListenerOptions { + capture?: boolean; +} +interface EventTargetAddEventListenerOptions { + capture?: boolean; + passive?: boolean; + once?: boolean; + signal?: AbortSignal; +} +interface EventTargetHandlerObject { + handleEvent: (event: Event) => any | undefined; +} +/** + * A controller object that allows you to abort one or more DOM requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +declare class AbortController { + constructor(); + /** + * Returns the AbortSignal object associated with this object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + get signal(): AbortSignal; + /** + * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} +/** + * A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +declare abstract class AbortSignal extends EventTarget { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */ + static abort(reason?: any): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */ + static timeout(delay: number): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) */ + static any(signals: AbortSignal[]): AbortSignal; + /** + * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + get aborted(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) */ + get reason(): any; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + get onabort(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + set onabort(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) */ + throwIfAborted(): void; +} +interface Scheduler { + wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise; +} +interface SchedulerWaitOptions { + signal?: AbortSignal; +} +/** + * Extends the lifetime of the install and activate events dispatched on the global scope as part of the service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it upgrades database schemas and deletes the outdated cache entries. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent) + */ +declare abstract class ExtendableEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) */ + waitUntil(promise: Promise): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent) */ +declare class CustomEvent extends Event { + constructor(type: string, init?: CustomEventCustomEventInit); + /** + * Returns any custom data event was created with. Typically used for synthetic events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) + */ + get detail(): T; +} +interface CustomEventCustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; +} +/** + * A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob) + */ +declare class Blob { + constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + get size(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + get type(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number, type?: string): Blob; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream) */ + stream(): ReadableStream; +} +interface BlobOptions { + type?: string; +} +/** + * Provides information about files and allows JavaScript in a web page to access their content. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File) + */ +declare class File extends Blob { + constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + get name(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + get lastModified(): number; +} +interface FileOptions { + type?: string; + lastModified?: number; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class CacheStorage { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open) */ + open(cacheName: string): Promise; + readonly default: Cache; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class Cache { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */ + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */ + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */ + put(request: RequestInfo | URL, response: Response): Promise; +} +interface CacheQueryOptions { + ignoreMethod?: boolean; +} +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare abstract class Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + get subtle(): SubtleCrypto; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(buffer: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): string; + DigestStream: typeof DigestStream; +} +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +declare abstract class SubtleCrypto { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: string, key: CryptoKey): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean; +} +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +declare abstract class CryptoKey { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: string[]; +} +interface CryptoKeyPair { + publicKey: CryptoKey; + privateKey: CryptoKey; +} +interface JsonWebKey { + kty: string; + use?: string; + key_ops?: string[]; + alg?: string; + ext?: boolean; + crv?: string; + x?: string; + y?: string; + d?: string; + n?: string; + e?: string; + p?: string; + q?: string; + dp?: string; + dq?: string; + qi?: string; + oth?: RsaOtherPrimesInfo[]; + k?: string; +} +interface RsaOtherPrimesInfo { + r?: string; + d?: string; + t?: string; +} +interface SubtleCryptoDeriveKeyAlgorithm { + name: string; + salt?: ArrayBuffer; + iterations?: number; + hash?: (string | SubtleCryptoHashAlgorithm); + $public?: CryptoKey; + info?: ArrayBuffer; +} +interface SubtleCryptoEncryptAlgorithm { + name: string; + iv?: ArrayBuffer; + additionalData?: ArrayBuffer; + tagLength?: number; + counter?: ArrayBuffer; + length?: number; + label?: ArrayBuffer; +} +interface SubtleCryptoGenerateKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + modulusLength?: number; + publicExponent?: ArrayBuffer; + length?: number; + namedCurve?: string; +} +interface SubtleCryptoHashAlgorithm { + name: string; +} +interface SubtleCryptoImportKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + length?: number; + namedCurve?: string; + compressed?: boolean; +} +interface SubtleCryptoSignAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + dataLength?: number; + saltLength?: number; +} +interface CryptoKeyKeyAlgorithm { + name: string; +} +interface CryptoKeyAesKeyAlgorithm { + name: string; + length: number; +} +interface CryptoKeyHmacKeyAlgorithm { + name: string; + hash: CryptoKeyKeyAlgorithm; + length: number; +} +interface CryptoKeyRsaKeyAlgorithm { + name: string; + modulusLength: number; + publicExponent: ArrayBuffer | (ArrayBuffer | ArrayBufferView); + hash?: CryptoKeyKeyAlgorithm; +} +interface CryptoKeyEllipticKeyAlgorithm { + name: string; + namedCurve: string; +} +interface CryptoKeyArbitraryKeyAlgorithm { + name: string; + hash?: CryptoKeyKeyAlgorithm; + namedCurve?: string; + length?: number; +} +declare class DigestStream extends WritableStream { + constructor(algorithm: string | SubtleCryptoHashAlgorithm); + get digest(): Promise; + get bytesWritten(): number | bigint; +} +/** + * A decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder) + */ +declare class TextDecoder { + constructor(decoder?: string, options?: TextDecoderConstructorOptions); + /** + * Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments. + * + * ``` + * var string = "", decoder = new TextDecoder(encoding), buffer; + * while(buffer = next_chunk()) { + * string += decoder.decode(buffer, {stream:true}); + * } + * string += decoder.decode(); // end-of-queue + * ``` + * + * If the error mode is "fatal" and encoding's decoder returns error, throws a TypeError. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode) + */ + decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string; + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +/** + * TextEncoder takes a stream of code points as input and emits a stream of bytes. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder) + */ +declare class TextEncoder { + constructor(); + /** + * Returns the result of running UTF-8's encoder. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode) + */ + encode(input?: string): Uint8Array; + /** + * Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto) + */ + encodeInto(input: string, buffer: ArrayBuffer | ArrayBufferView): TextEncoderEncodeIntoResult; + get encoding(): string; +} +interface TextDecoderConstructorOptions { + fatal: boolean; + ignoreBOM: boolean; +} +interface TextDecoderDecodeOptions { + stream: boolean; +} +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} +/** + * Events providing information related to errors in scripts or in files. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent) + */ +declare class ErrorEvent extends Event { + constructor(type: string, init?: ErrorEventErrorEventInit); + get filename(): string; + get message(): string; + get lineno(): number; + get colno(): number; + get error(): any; +} +interface ErrorEventErrorEventInit { + message?: string; + filename?: string; + lineno?: number; + colno?: number; + error?: any; +} +/** + * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData) + */ +declare class FormData { + constructor(); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: Blob, filename?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete) */ + delete(name: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get) */ + get(name: string): (File | string) | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll) */ + getAll(name: string): (File | string)[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: Blob, filename?: string): void; + /* Returns an array of key, value pairs for every entry in the list. */ + entries(): IterableIterator<[ + key: string, + value: File | string + ]>; + /* Returns a list of keys in the list. */ + keys(): IterableIterator; + /* Returns a list of values in the list. */ + values(): IterableIterator<(File | string)>; + forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: File | string + ]>; +} +interface ContentOptions { + html?: boolean; +} +declare class HTMLRewriter { + constructor(); + on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter; + onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter; + transform(response: Response): Response; +} +interface HTMLRewriterElementContentHandlers { + element?(element: Element): void | Promise; + comments?(comment: Comment): void | Promise; + text?(element: Text): void | Promise; +} +interface HTMLRewriterDocumentContentHandlers { + doctype?(doctype: Doctype): void | Promise; + comments?(comment: Comment): void | Promise; + text?(text: Text): void | Promise; + end?(end: DocumentEnd): void | Promise; +} +interface Doctype { + readonly name: string | null; + readonly publicId: string | null; + readonly systemId: string | null; +} +interface Element { + tagName: string; + readonly attributes: IterableIterator; + readonly removed: boolean; + readonly namespaceURI: string; + getAttribute(name: string): string | null; + hasAttribute(name: string): boolean; + setAttribute(name: string, value: string): Element; + removeAttribute(name: string): Element; + before(content: string | ReadableStream | Response, options?: ContentOptions): Element; + after(content: string | ReadableStream | Response, options?: ContentOptions): Element; + prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element; + append(content: string | ReadableStream | Response, options?: ContentOptions): Element; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Element; + remove(): Element; + removeAndKeepContent(): Element; + setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element; + onEndTag(handler: (tag: EndTag) => void | Promise): void; +} +interface EndTag { + name: string; + before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + remove(): EndTag; +} +interface Comment { + text: string; + readonly removed: boolean; + before(content: string, options?: ContentOptions): Comment; + after(content: string, options?: ContentOptions): Comment; + replace(content: string, options?: ContentOptions): Comment; + remove(): Comment; +} +interface Text { + readonly text: string; + readonly lastInTextNode: boolean; + readonly removed: boolean; + before(content: string | ReadableStream | Response, options?: ContentOptions): Text; + after(content: string | ReadableStream | Response, options?: ContentOptions): Text; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Text; + remove(): Text; +} +interface DocumentEnd { + append(content: string, options?: ContentOptions): DocumentEnd; +} +/** + * This is the event type for fetch events dispatched on the service worker global scope. It contains information about the fetch, including the request and how the receiver will treat the response. It provides the event.respondWith() method, which allows us to provide a response to this fetch. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent) + */ +declare abstract class FetchEvent extends ExtendableEvent { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request) */ + readonly request: Request; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */ + respondWith(promise: Response | Promise): void; + passThroughOnException(): void; +} +type HeadersInit = Headers | Iterable> | Record; +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers) + */ +declare class Headers { + constructor(init?: HeadersInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ + get(name: string): string | null; + getAll(name: string): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ + getSetCookie(): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ + delete(name: string): void; + forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void; + /* Returns an iterator allowing to go through all key/value pairs contained in this object. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */ + keys(): IterableIterator; + /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */ + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData; +declare abstract class Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ + get body(): ReadableStream | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + get bodyUsed(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ + json(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ + formData(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob(): Promise; +} +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +declare var Response: { + prototype: Response; + new (body?: BodyInit | null, init?: ResponseInit): Response; + error(): Response; + redirect(url: string, status?: number): Response; + json(any: any, maybeInit?: (ResponseInit | Response)): Response; +}; +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +interface Response extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone) */ + clone(): Response; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status) */ + status: number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText) */ + statusText: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers) */ + headers: Headers; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok) */ + ok: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected) */ + redirected: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url) */ + url: string; + webSocket: WebSocket | null; + cf: any | undefined; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type) */ + type: "default" | "error"; +} +interface ResponseInit { + status?: number; + statusText?: string; + headers?: HeadersInit; + cf?: any; + webSocket?: (WebSocket | null); + encodeBody?: "automatic" | "manual"; +} +type RequestInfo> = Request | string; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +declare var Request: { + prototype: Request; + new >(input: RequestInfo | URL, init?: RequestInit): Request; +}; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +interface Request> extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone) */ + clone(): Request; + /** + * Returns request's HTTP method, which is "GET" by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method) + */ + method: string; + /** + * Returns the URL of request as a string. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url) + */ + url: string; + /** + * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers) + */ + headers: Headers; + /** + * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect) + */ + redirect: string; + fetcher: Fetcher | null; + /** + * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal) + */ + signal: AbortSignal; + cf: Cf | undefined; + /** + * Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity) + */ + integrity: string; + /* Returns a boolean indicating whether or not request can outlive the global in which it was created. */ + keepalive: boolean; + /** + * Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache) + */ + cache?: "no-store"; +} +interface RequestInit { + /* A string to set request's method. */ + method?: string; + /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /* A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: string; + fetcher?: (Fetcher | null); + cf?: Cf; + /* A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: "no-store"; + /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /* An AbortSignal to set request's signal. */ + signal?: (AbortSignal | null); + encodeResponseBody?: "automatic" | "manual"; +} +type Service = Fetcher; +type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & { + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + connect(address: SocketAddress | string, options?: SocketOptions): Socket; +}; +interface KVNamespaceListKey { + name: Key; + expiration?: number; + metadata?: Metadata; +} +type KVNamespaceListResult = { + list_complete: false; + keys: KVNamespaceListKey[]; + cursor: string; + cacheStatus: string | null; +} | { + list_complete: true; + keys: KVNamespaceListKey[]; + cacheStatus: string | null; +}; +interface KVNamespace { + get(key: Key, options?: Partial>): Promise; + get(key: Key, type: "text"): Promise; + get(key: Key, type: "json"): Promise; + get(key: Key, type: "arrayBuffer"): Promise; + get(key: Key, type: "stream"): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"text">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"json">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"arrayBuffer">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"stream">): Promise; + list(options?: KVNamespaceListOptions): Promise>; + put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise; + getWithMetadata(key: Key, options?: Partial>): Promise>; + getWithMetadata(key: Key, type: "text"): Promise>; + getWithMetadata(key: Key, type: "json"): Promise>; + getWithMetadata(key: Key, type: "arrayBuffer"): Promise>; + getWithMetadata(key: Key, type: "stream"): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"text">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"json">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"arrayBuffer">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"stream">): Promise>; + delete(key: Key): Promise; +} +interface KVNamespaceListOptions { + limit?: number; + prefix?: (string | null); + cursor?: (string | null); +} +interface KVNamespaceGetOptions { + type: Type; + cacheTtl?: number; +} +interface KVNamespacePutOptions { + expiration?: number; + expirationTtl?: number; + metadata?: (any | null); +} +interface KVNamespaceGetWithMetadataResult { + value: Value | null; + metadata: Metadata | null; + cacheStatus: string | null; +} +type QueueContentType = "text" | "bytes" | "json" | "v8"; +interface Queue { + send(message: Body, options?: QueueSendOptions): Promise; + sendBatch(messages: Iterable>, options?: QueueSendBatchOptions): Promise; +} +interface QueueSendOptions { + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueSendBatchOptions { + delaySeconds?: number; +} +interface MessageSendRequest { + body: Body; + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueRetryOptions { + delaySeconds?: number; +} +interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + readonly attempts: number; + retry(options?: QueueRetryOptions): void; + ack(): void; +} +interface QueueEvent extends ExtendableEvent { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface MessageBatch { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface R2Error extends Error { + readonly name: string; + readonly code: number; + readonly message: string; + readonly action: string; + readonly stack: any; +} +interface R2ListOptions { + limit?: number; + prefix?: string; + cursor?: string; + delimiter?: string; + startAfter?: string; + include?: ("httpMetadata" | "customMetadata")[]; +} +declare abstract class R2Bucket { + head(key: string): Promise; + get(key: string, options: R2GetOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + get(key: string, options?: R2GetOptions): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise; + createMultipartUpload(key: string, options?: R2MultipartOptions): Promise; + resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload; + delete(keys: string | string[]): Promise; + list(options?: R2ListOptions): Promise; +} +interface R2MultipartUpload { + readonly key: string; + readonly uploadId: string; + uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise; + abort(): Promise; + complete(uploadedParts: R2UploadedPart[]): Promise; +} +interface R2UploadedPart { + partNumber: number; + etag: string; +} +declare abstract class R2Object { + readonly key: string; + readonly version: string; + readonly size: number; + readonly etag: string; + readonly httpEtag: string; + readonly checksums: R2Checksums; + readonly uploaded: Date; + readonly httpMetadata?: R2HTTPMetadata; + readonly customMetadata?: Record; + readonly range?: R2Range; + readonly storageClass: string; + readonly ssecKeyMd5?: string; + writeHttpMetadata(headers: Headers): void; +} +interface R2ObjectBody extends R2Object { + get body(): ReadableStream; + get bodyUsed(): boolean; + arrayBuffer(): Promise; + text(): Promise; + json(): Promise; + blob(): Promise; +} +type R2Range = { + offset: number; + length?: number; +} | { + offset?: number; + length: number; +} | { + suffix: number; +}; +interface R2Conditional { + etagMatches?: string; + etagDoesNotMatch?: string; + uploadedBefore?: Date; + uploadedAfter?: Date; + secondsGranularity?: boolean; +} +interface R2GetOptions { + onlyIf?: (R2Conditional | Headers); + range?: (R2Range | Headers); + ssecKey?: (ArrayBuffer | string); +} +interface R2PutOptions { + onlyIf?: (R2Conditional | Headers); + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + md5?: (ArrayBuffer | string); + sha1?: (ArrayBuffer | string); + sha256?: (ArrayBuffer | string); + sha384?: (ArrayBuffer | string); + sha512?: (ArrayBuffer | string); + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2MultipartOptions { + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2Checksums { + readonly md5?: ArrayBuffer; + readonly sha1?: ArrayBuffer; + readonly sha256?: ArrayBuffer; + readonly sha384?: ArrayBuffer; + readonly sha512?: ArrayBuffer; + toJSON(): R2StringChecksums; +} +interface R2StringChecksums { + md5?: string; + sha1?: string; + sha256?: string; + sha384?: string; + sha512?: string; +} +interface R2HTTPMetadata { + contentType?: string; + contentLanguage?: string; + contentDisposition?: string; + contentEncoding?: string; + cacheControl?: string; + cacheExpiry?: Date; +} +type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ({ + truncated: true; + cursor: string; +} | { + truncated: false; +}); +interface R2UploadPartOptions { + ssecKey?: (ArrayBuffer | string); +} +declare abstract class ScheduledEvent extends ExtendableEvent { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface ScheduledController { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface QueuingStrategy { + highWaterMark?: (number | bigint); + size?: (chunk: T) => number | bigint; +} +interface UnderlyingSink { + type?: string; + start?: (controller: WritableStreamDefaultController) => void | Promise; + write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise; + abort?: (reason: any) => void | Promise; + close?: () => void | Promise; +} +interface UnderlyingByteSource { + type: "bytes"; + autoAllocateChunkSize?: number; + start?: (controller: ReadableByteStreamController) => void | Promise; + pull?: (controller: ReadableByteStreamController) => void | Promise; + cancel?: (reason: any) => void | Promise; +} +interface UnderlyingSource { + type?: "" | undefined; + start?: (controller: ReadableStreamDefaultController) => void | Promise; + pull?: (controller: ReadableStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: (number | bigint); +} +interface Transformer { + readableType?: string; + writableType?: string; + start?: (controller: TransformStreamDefaultController) => void | Promise; + transform?: (chunk: I, controller: TransformStreamDefaultController) => void | Promise; + flush?: (controller: TransformStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: number; +} +interface StreamPipeOptions { + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + preventAbort?: boolean; + preventCancel?: boolean; + signal?: AbortSignal; +} +type ReadableStreamReadResult = { + done: false; + value: R; +} | { + done: true; + value?: undefined; +}; +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +interface ReadableStream { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel) */ + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(): ReadableStreamDefaultReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough) */ + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo) */ + pipeTo(destination: WritableStream, options?: StreamPipeOptions): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee) */ + tee(): [ + ReadableStream, + ReadableStream + ]; + values(options?: ReadableStreamValuesOptions): AsyncIterableIterator; + [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator; +} +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +declare const ReadableStream: { + prototype: ReadableStream; + new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy): ReadableStream; + new (underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; +}; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader) */ +declare class ReadableStreamDefaultReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read) */ + read(): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */ +declare class ReadableStreamBYOBReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read) */ + read(view: T): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock) */ + releaseLock(): void; + readAtLeast(minElements: number, view: T): Promise>; +} +interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions { + min?: number; +} +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode: "byob"; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest) */ +declare abstract class ReadableStreamBYOBRequest { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view) */ + get view(): Uint8Array | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond) */ + respond(bytesWritten: number): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView) */ + respondWithNewView(view: ArrayBuffer | ArrayBufferView): void; + get atLeast(): number | null; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController) */ +declare abstract class ReadableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue) */ + enqueue(chunk?: R): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error) */ + error(reason: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController) */ +declare abstract class ReadableByteStreamController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest) */ + get byobRequest(): ReadableStreamBYOBRequest | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue) */ + enqueue(chunk: ArrayBuffer | ArrayBufferView): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error) */ + error(reason: any): void; +} +/** + * This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController) + */ +declare abstract class WritableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal) */ + get signal(): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error) */ + error(reason?: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController) */ +declare abstract class TransformStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue) */ + enqueue(chunk?: O): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error) */ + error(reason: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate) */ + terminate(): void; +} +interface ReadableWritablePair { + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; + readable: ReadableStream; +} +/** + * This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream) + */ +declare class WritableStream { + constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter) */ + getWriter(): WritableStreamDefaultWriter; +} +/** + * This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter) + */ +declare class WritableStreamDefaultWriter { + constructor(stream: WritableStream); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed) */ + get closed(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready) */ + get ready(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write) */ + write(chunk?: W): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream) */ +declare class TransformStream { + constructor(transformer?: Transformer, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable) */ + get readable(): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable) */ + get writable(): WritableStream; +} +declare class FixedLengthStream extends IdentityTransformStream { + constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +declare class IdentityTransformStream extends TransformStream { + constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +interface IdentityTransformStreamQueuingStrategy { + highWaterMark?: (number | bigint); +} +interface ReadableStreamValuesOptions { + preventCancel?: boolean; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream) */ +declare class CompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream) */ +declare class DecompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream) */ +declare class TextEncoderStream extends TransformStream { + constructor(); + get encoding(): string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream) */ +declare class TextDecoderStream extends TransformStream { + constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit); + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +interface TextDecoderStreamTextDecoderStreamInit { + fatal?: boolean; + ignoreBOM?: boolean; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy) + */ +declare class ByteLengthQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy) + */ +declare class CountQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} +interface ScriptVersion { + id?: string; + tag?: string; + message?: string; +} +declare abstract class TailEvent extends ExtendableEvent { + readonly events: TraceItem[]; + readonly traces: TraceItem[]; +} +interface TraceItem { + readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null; + readonly eventTimestamp: number | null; + readonly logs: TraceLog[]; + readonly exceptions: TraceException[]; + readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; + readonly scriptName: string | null; + readonly entrypoint?: string; + readonly scriptVersion?: ScriptVersion; + readonly dispatchNamespace?: string; + readonly scriptTags?: string[]; + readonly outcome: string; + readonly executionModel: string; + readonly truncated: boolean; + readonly cpuTime: number; + readonly wallTime: number; +} +interface TraceItemAlarmEventInfo { + readonly scheduledTime: Date; +} +interface TraceItemCustomEventInfo { +} +interface TraceItemScheduledEventInfo { + readonly scheduledTime: number; + readonly cron: string; +} +interface TraceItemQueueEventInfo { + readonly queue: string; + readonly batchSize: number; +} +interface TraceItemEmailEventInfo { + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; +} +interface TraceItemTailEventInfo { + readonly consumedEvents: TraceItemTailEventInfoTailItem[]; +} +interface TraceItemTailEventInfoTailItem { + readonly scriptName: string | null; +} +interface TraceItemFetchEventInfo { + readonly response?: TraceItemFetchEventInfoResponse; + readonly request: TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoRequest { + readonly cf?: any; + readonly headers: Record; + readonly method: string; + readonly url: string; + getUnredacted(): TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoResponse { + readonly status: number; +} +interface TraceItemJsRpcEventInfo { + readonly rpcMethod: string; +} +interface TraceItemHibernatableWebSocketEventInfo { + readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError; +} +interface TraceItemHibernatableWebSocketEventInfoMessage { + readonly webSocketEventType: string; +} +interface TraceItemHibernatableWebSocketEventInfoClose { + readonly webSocketEventType: string; + readonly code: number; + readonly wasClean: boolean; +} +interface TraceItemHibernatableWebSocketEventInfoError { + readonly webSocketEventType: string; +} +interface TraceLog { + readonly timestamp: number; + readonly level: string; + readonly message: any; +} +interface TraceException { + readonly timestamp: number; + readonly message: string; + readonly name: string; + readonly stack?: string; +} +interface TraceDiagnosticChannelEvent { + readonly timestamp: number; + readonly channel: string; + readonly message: any; +} +interface TraceMetrics { + readonly cpuTime: number; + readonly wallTime: number; +} +interface UnsafeTraceMetrics { + fromTrace(item: TraceItem): TraceMetrics; +} +/** + * The URL interface represents an object providing static methods used for creating object URLs. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL) + */ +declare class URL { + constructor(url: string | URL, base?: string | URL); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin) */ + get origin(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + get href(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + set href(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + get protocol(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + set protocol(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + get username(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + set username(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + get password(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + set password(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + get host(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + set host(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + get hostname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + set hostname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + get port(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + set port(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + get pathname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + set pathname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + get search(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + set search(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + get hash(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + set hash(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams) */ + get searchParams(): URLSearchParams; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON) */ + toJSON(): string; + /*function toString() { [native code] }*/ + toString(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static) */ + static canParse(url: string, base?: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static) */ + static parse(url: string, base?: string): URL | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static) */ + static createObjectURL(object: File | Blob): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static) */ + static revokeObjectURL(object_url: string): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams) */ +declare class URLSearchParams { + constructor(init?: (Iterable> | Record | string)); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size) */ + get size(): number; + /** + * Appends a specified key/value pair as a new search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) + */ + delete(name: string, value?: string): void; + /** + * Returns the first value associated to the given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) + */ + has(name: string, value?: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) + */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort) */ + sort(): void; + /* Returns an array of key, value pairs for every entry in the search params. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns a list of keys in the search params. */ + keys(): IterableIterator; + /* Returns a list of values in the search params. */ + values(): IterableIterator; + forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void; + /*function toString() { [native code] } Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */ + toString(): string; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +declare class URLPattern { + constructor(input?: (string | URLPatternURLPatternInit), baseURL?: (string | URLPatternURLPatternOptions), patternOptions?: URLPatternURLPatternOptions); + get protocol(): string; + get username(): string; + get password(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get hash(): string; + test(input?: (string | URLPatternURLPatternInit), baseURL?: string): boolean; + exec(input?: (string | URLPatternURLPatternInit), baseURL?: string): URLPatternURLPatternResult | null; +} +interface URLPatternURLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} +interface URLPatternURLPatternComponentResult { + input: string; + groups: Record; +} +interface URLPatternURLPatternResult { + inputs: (string | URLPatternURLPatternInit)[]; + protocol: URLPatternURLPatternComponentResult; + username: URLPatternURLPatternComponentResult; + password: URLPatternURLPatternComponentResult; + hostname: URLPatternURLPatternComponentResult; + port: URLPatternURLPatternComponentResult; + pathname: URLPatternURLPatternComponentResult; + search: URLPatternURLPatternComponentResult; + hash: URLPatternURLPatternComponentResult; +} +interface URLPatternURLPatternOptions { + ignoreCase?: boolean; +} +/** + * A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent) + */ +declare class CloseEvent extends Event { + constructor(type: string, initializer?: CloseEventInit); + /** + * Returns the WebSocket connection close code provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code) + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason) + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean) + */ + readonly wasClean: boolean; +} +interface CloseEventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} +/** + * A message received by a target object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent) + */ +declare class MessageEvent extends Event { + constructor(type: string, initializer: MessageEventInit); + /** + * Returns the data of the message. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data) + */ + readonly data: ArrayBuffer | string; +} +interface MessageEventInit { + data: ArrayBuffer | string; +} +type WebSocketEventMap = { + close: CloseEvent; + message: MessageEvent; + open: Event; + error: ErrorEvent; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +declare var WebSocket: { + prototype: WebSocket; + new (url: string, protocols?: (string[] | string)): WebSocket; + readonly READY_STATE_CONNECTING: number; + readonly CONNECTING: number; + readonly READY_STATE_OPEN: number; + readonly OPEN: number; + readonly READY_STATE_CLOSING: number; + readonly CLOSING: number; + readonly READY_STATE_CLOSED: number; + readonly CLOSED: number; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +interface WebSocket extends EventTarget { + accept(): void; + /** + * Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send) + */ + send(message: (ArrayBuffer | ArrayBufferView) | string): void; + /** + * Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close) + */ + close(code?: number, reason?: string): void; + serializeAttachment(attachment: any): void; + deserializeAttachment(): any | null; + /** + * Returns the state of the WebSocket object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState) + */ + readyState: number; + /** + * Returns the URL that was used to establish the WebSocket connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url) + */ + url: string | null; + /** + * Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol) + */ + protocol: string | null; + /** + * Returns the extensions selected by the server, if any. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions) + */ + extensions: string | null; +} +declare const WebSocketPair: { + new (): { + 0: WebSocket; + 1: WebSocket; + }; +}; +interface SqlStorage { + exec>(query: string, ...bindings: any[]): SqlStorageCursor; + get databaseSize(): number; + Cursor: typeof SqlStorageCursor; + Statement: typeof SqlStorageStatement; +} +declare abstract class SqlStorageStatement { +} +type SqlStorageValue = ArrayBuffer | string | number | null; +declare abstract class SqlStorageCursor> { + next(): { + done?: false; + value: T; + } | { + done: true; + value?: never; + }; + toArray(): T[]; + one(): T; + raw(): IterableIterator; + columnNames: string[]; + get rowsRead(): number; + get rowsWritten(): number; + [Symbol.iterator](): IterableIterator; +} +interface Socket { + get readable(): ReadableStream; + get writable(): WritableStream; + get closed(): Promise; + get opened(): Promise; + close(): Promise; + startTls(options?: TlsOptions): Socket; +} +interface SocketOptions { + secureTransport?: string; + allowHalfOpen: boolean; + highWaterMark?: (number | bigint); +} +interface SocketAddress { + hostname: string; + port: number; +} +interface TlsOptions { + expectedServerHostname?: string; +} +interface SocketInfo { + remoteAddress?: string; + localAddress?: string; +} +interface GPU { + requestAdapter(param1?: GPURequestAdapterOptions): Promise; +} +declare abstract class GPUAdapter { + requestDevice(param1?: GPUDeviceDescriptor): Promise; + requestAdapterInfo(unmaskHints?: string[]): Promise; + get features(): GPUSupportedFeatures; + get limits(): GPUSupportedLimits; +} +interface GPUDevice extends EventTarget { + createBuffer(param1: GPUBufferDescriptor): GPUBuffer; + createBindGroupLayout(descriptor: GPUBindGroupLayoutDescriptor): GPUBindGroupLayout; + createBindGroup(descriptor: GPUBindGroupDescriptor): GPUBindGroup; + createSampler(descriptor: GPUSamplerDescriptor): GPUSampler; + createShaderModule(descriptor: GPUShaderModuleDescriptor): GPUShaderModule; + createPipelineLayout(descriptor: GPUPipelineLayoutDescriptor): GPUPipelineLayout; + createComputePipeline(descriptor: GPUComputePipelineDescriptor): GPUComputePipeline; + createRenderPipeline(descriptor: GPURenderPipelineDescriptor): GPURenderPipeline; + createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): GPUCommandEncoder; + createTexture(param1: GPUTextureDescriptor): GPUTexture; + destroy(): void; + createQuerySet(descriptor: GPUQuerySetDescriptor): GPUQuerySet; + pushErrorScope(filter: string): void; + popErrorScope(): Promise; + get queue(): GPUQueue; + get lost(): Promise; + get features(): GPUSupportedFeatures; + get limits(): GPUSupportedLimits; +} +interface GPUDeviceDescriptor { + label?: string; + requiredFeatures?: string[]; + requiredLimits?: Record; + defaultQueue?: GPUQueueDescriptor; +} +interface GPUBufferDescriptor { + label: string; + size: number | bigint; + usage: number; + mappedAtCreation: boolean; +} +interface GPUQueueDescriptor { + label?: string; +} +declare abstract class GPUBufferUsage { + static readonly MAP_READ: number; + static readonly MAP_WRITE: number; + static readonly COPY_SRC: number; + static readonly COPY_DST: number; + static readonly INDEX: number; + static readonly VERTEX: number; + static readonly UNIFORM: number; + static readonly STORAGE: number; + static readonly INDIRECT: number; + static readonly QUERY_RESOLVE: number; +} +interface GPUBuffer { + getMappedRange(size?: (number | bigint), param2?: (number | bigint)): ArrayBuffer; + unmap(): void; + destroy(): void; + mapAsync(offset: number, size?: (number | bigint), param3?: (number | bigint)): Promise; + get size(): number | bigint; + get usage(): number; + get mapState(): string; +} +declare abstract class GPUShaderStage { + static readonly VERTEX: number; + static readonly FRAGMENT: number; + static readonly COMPUTE: number; +} +interface GPUBindGroupLayoutDescriptor { + label?: string; + entries: GPUBindGroupLayoutEntry[]; +} +interface GPUBindGroupLayoutEntry { + binding: number; + visibility: number; + buffer?: GPUBufferBindingLayout; + sampler?: GPUSamplerBindingLayout; + texture?: GPUTextureBindingLayout; + storageTexture?: GPUStorageTextureBindingLayout; +} +interface GPUStorageTextureBindingLayout { + access?: string; + format: string; + viewDimension?: string; +} +interface GPUTextureBindingLayout { + sampleType?: string; + viewDimension?: string; + multisampled?: boolean; +} +interface GPUSamplerBindingLayout { + type?: string; +} +interface GPUBufferBindingLayout { + type?: string; + hasDynamicOffset?: boolean; + minBindingSize?: (number | bigint); +} +interface GPUBindGroupLayout { +} +interface GPUBindGroup { +} +interface GPUBindGroupDescriptor { + label?: string; + layout: GPUBindGroupLayout; + entries: GPUBindGroupEntry[]; +} +interface GPUBindGroupEntry { + binding: number; + resource: GPUBufferBinding | GPUSampler; +} +interface GPUBufferBinding { + buffer: GPUBuffer; + offset?: (number | bigint); + size?: (number | bigint); +} +interface GPUSampler { +} +interface GPUSamplerDescriptor { + label?: string; + addressModeU?: string; + addressModeV?: string; + addressModeW?: string; + magFilter?: string; + minFilter?: string; + mipmapFilter?: string; + lodMinClamp?: number; + lodMaxClamp?: number; + compare: string; + maxAnisotropy?: number; +} +interface GPUShaderModule { + getCompilationInfo(): Promise; +} +interface GPUShaderModuleDescriptor { + label?: string; + code: string; +} +interface GPUPipelineLayout { +} +interface GPUPipelineLayoutDescriptor { + label?: string; + bindGroupLayouts: GPUBindGroupLayout[]; +} +interface GPUComputePipeline { + getBindGroupLayout(index: number): GPUBindGroupLayout; +} +interface GPUComputePipelineDescriptor { + label?: string; + compute: GPUProgrammableStage; + layout: string | GPUPipelineLayout; +} +interface GPUProgrammableStage { + module: GPUShaderModule; + entryPoint: string; + constants?: Record; +} +interface GPUCommandEncoder { + get label(): string; + beginComputePass(descriptor?: GPUComputePassDescriptor): GPUComputePassEncoder; + beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder; + copyBufferToBuffer(source: GPUBuffer, sourceOffset: number | bigint, destination: GPUBuffer, destinationOffset: number | bigint, size: number | bigint): void; + finish(param0?: GPUCommandBufferDescriptor): GPUCommandBuffer; + copyTextureToBuffer(source: GPUImageCopyTexture, destination: GPUImageCopyBuffer, copySize: Iterable | GPUExtent3DDict): void; + copyBufferToTexture(source: GPUImageCopyBuffer, destination: GPUImageCopyTexture, copySize: Iterable | GPUExtent3DDict): void; + copyTextureToTexture(source: GPUImageCopyTexture, destination: GPUImageCopyTexture, copySize: Iterable | GPUExtent3DDict): void; + clearBuffer(buffer: GPUBuffer, offset?: (number | bigint), size?: (number | bigint)): void; +} +interface GPUCommandEncoderDescriptor { + label?: string; +} +interface GPUComputePassEncoder { + setPipeline(pipeline: GPUComputePipeline): void; + setBindGroup(index: number, bindGroup: GPUBindGroup | null, dynamicOffsets?: Iterable): void; + dispatchWorkgroups(workgroupCountX: number, workgroupCountY?: number, workgroupCountZ?: number): void; + end(): void; +} +interface GPUComputePassDescriptor { + label?: string; + timestampWrites?: GPUComputePassTimestampWrites; +} +interface GPUQuerySet { +} +interface GPUQuerySetDescriptor { + label?: string; +} +interface GPUComputePassTimestampWrites { + querySet: GPUQuerySet; + beginningOfPassWriteIndex?: number; + endOfPassWriteIndex?: number; +} +interface GPUCommandBufferDescriptor { + label?: string; +} +interface GPUCommandBuffer { +} +interface GPUQueue { + submit(commandBuffers: GPUCommandBuffer[]): void; + writeBuffer(buffer: GPUBuffer, bufferOffset: number | bigint, data: ArrayBuffer | ArrayBufferView, dataOffset?: (number | bigint), size?: (number | bigint)): void; +} +declare abstract class GPUMapMode { + static readonly READ: number; + static readonly WRITE: number; +} +interface GPURequestAdapterOptions { + powerPreference: string; + forceFallbackAdapter?: boolean; +} +interface GPUAdapterInfo { + get vendor(): string; + get architecture(): string; + get device(): string; + get description(): string; +} +interface GPUSupportedFeatures { + has(name: string): boolean; + keys(): string[]; +} +interface GPUSupportedLimits { + get maxTextureDimension1D(): number; + get maxTextureDimension2D(): number; + get maxTextureDimension3D(): number; + get maxTextureArrayLayers(): number; + get maxBindGroups(): number; + get maxBindingsPerBindGroup(): number; + get maxDynamicUniformBuffersPerPipelineLayout(): number; + get maxDynamicStorageBuffersPerPipelineLayout(): number; + get maxSampledTexturesPerShaderStage(): number; + get maxSamplersPerShaderStage(): number; + get maxStorageBuffersPerShaderStage(): number; + get maxStorageTexturesPerShaderStage(): number; + get maxUniformBuffersPerShaderStage(): number; + get maxUniformBufferBindingSize(): number | bigint; + get maxStorageBufferBindingSize(): number | bigint; + get minUniformBufferOffsetAlignment(): number; + get minStorageBufferOffsetAlignment(): number; + get maxVertexBuffers(): number; + get maxBufferSize(): number | bigint; + get maxVertexAttributes(): number; + get maxVertexBufferArrayStride(): number; + get maxInterStageShaderComponents(): number; + get maxInterStageShaderVariables(): number; + get maxColorAttachments(): number; + get maxColorAttachmentBytesPerSample(): number; + get maxComputeWorkgroupStorageSize(): number; + get maxComputeInvocationsPerWorkgroup(): number; + get maxComputeWorkgroupSizeX(): number; + get maxComputeWorkgroupSizeY(): number; + get maxComputeWorkgroupSizeZ(): number; + get maxComputeWorkgroupsPerDimension(): number; +} +declare abstract class GPUError { + get message(): string; +} +declare abstract class GPUOutOfMemoryError extends GPUError { +} +declare abstract class GPUInternalError extends GPUError { +} +declare abstract class GPUValidationError extends GPUError { +} +declare abstract class GPUDeviceLostInfo { + get message(): string; + get reason(): string; +} +interface GPUCompilationMessage { + get message(): string; + get type(): string; + get lineNum(): number; + get linePos(): number; + get offset(): number; + get length(): number; +} +interface GPUCompilationInfo { + get messages(): GPUCompilationMessage[]; +} +declare abstract class GPUTextureUsage { + static readonly COPY_SRC: number; + static readonly COPY_DST: number; + static readonly TEXTURE_BINDING: number; + static readonly STORAGE_BINDING: number; + static readonly RENDER_ATTACHMENT: number; +} +interface GPUTextureDescriptor { + label: string; + size: number[] | GPUExtent3DDict; + mipLevelCount?: number; + sampleCount?: number; + dimension?: string; + format: string; + usage: number; + viewFormats?: string[]; +} +interface GPUExtent3DDict { + width: number; + height?: number; + depthOrArrayLayers?: number; +} +interface GPUTexture { + createView(descriptor?: GPUTextureViewDescriptor): GPUTextureView; + destroy(): void; + get width(): number; + get height(): number; + get depthOrArrayLayers(): number; + get mipLevelCount(): number; + get dimension(): string; + get format(): string; + get usage(): number; +} +interface GPUTextureView { +} +interface GPUTextureViewDescriptor { + label: string; + format: string; + dimension: string; + aspect?: string; + baseMipLevel?: number; + mipLevelCount: number; + baseArrayLayer?: number; + arrayLayerCount: number; +} +declare abstract class GPUColorWrite { + static readonly RED: number; + static readonly GREEN: number; + static readonly BLUE: number; + static readonly ALPHA: number; + static readonly ALL: number; +} +interface GPURenderPipeline { +} +interface GPURenderPipelineDescriptor { + label?: string; + layout: string | GPUPipelineLayout; + vertex: GPUVertexState; + primitive?: GPUPrimitiveState; + depthStencil?: GPUDepthStencilState; + multisample?: GPUMultisampleState; + fragment?: GPUFragmentState; +} +interface GPUVertexState { + module: GPUShaderModule; + entryPoint: string; + constants?: Record; + buffers?: GPUVertexBufferLayout[]; +} +interface GPUVertexBufferLayout { + arrayStride: number | bigint; + stepMode?: string; + attributes: GPUVertexAttribute[]; +} +interface GPUVertexAttribute { + format: string; + offset: number | bigint; + shaderLocation: number; +} +interface GPUPrimitiveState { + topology?: string; + stripIndexFormat?: string; + frontFace?: string; + cullMode?: string; + unclippedDepth?: boolean; +} +interface GPUStencilFaceState { + compare?: string; + failOp?: string; + depthFailOp?: string; + passOp?: string; +} +interface GPUDepthStencilState { + format: string; + depthWriteEnabled: boolean; + depthCompare: string; + stencilFront?: GPUStencilFaceState; + stencilBack?: GPUStencilFaceState; + stencilReadMask?: number; + stencilWriteMask?: number; + depthBias?: number; + depthBiasSlopeScale?: number; + depthBiasClamp?: number; +} +interface GPUMultisampleState { + count?: number; + mask?: number; + alphaToCoverageEnabled?: boolean; +} +interface GPUFragmentState { + module: GPUShaderModule; + entryPoint: string; + constants?: Record; + targets: GPUColorTargetState[]; +} +interface GPUColorTargetState { + format: string; + blend: GPUBlendState; + writeMask?: number; +} +interface GPUBlendState { + color: GPUBlendComponent; + alpha: GPUBlendComponent; +} +interface GPUBlendComponent { + operation?: string; + srcFactor?: string; + dstFactor?: string; +} +interface GPURenderPassEncoder { + setPipeline(pipeline: GPURenderPipeline): void; + draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void; + end(): void; +} +interface GPURenderPassDescriptor { + label?: string; + colorAttachments: GPURenderPassColorAttachment[]; + depthStencilAttachment?: GPURenderPassDepthStencilAttachment; + occlusionQuerySet?: GPUQuerySet; + timestampWrites?: GPURenderPassTimestampWrites; + maxDrawCount?: (number | bigint); +} +interface GPURenderPassColorAttachment { + view: GPUTextureView; + depthSlice?: number; + resolveTarget?: GPUTextureView; + clearValue?: (number[] | GPUColorDict); + loadOp: string; + storeOp: string; +} +interface GPUColorDict { + r: number; + g: number; + b: number; + a: number; +} +interface GPURenderPassDepthStencilAttachment { + view: GPUTextureView; + depthClearValue?: number; + depthLoadOp?: string; + depthStoreOp?: string; + depthReadOnly?: boolean; + stencilClearValue?: number; + stencilLoadOp?: string; + stencilStoreOp?: string; + stencilReadOnly?: boolean; +} +interface GPURenderPassTimestampWrites { + querySet: GPUQuerySet; + beginningOfPassWriteIndex?: number; + endOfPassWriteIndex?: number; +} +interface GPUImageCopyTexture { + texture: GPUTexture; + mipLevel?: number; + origin?: (number[] | GPUOrigin3DDict); + aspect?: string; +} +interface GPUImageCopyBuffer { + buffer: GPUBuffer; + offset?: (number | bigint); + bytesPerRow?: number; + rowsPerImage?: number; +} +interface GPUOrigin3DDict { + x?: number; + y?: number; + z?: number; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource) */ +declare class EventSource extends EventTarget { + constructor(url: string, init?: EventSourceEventSourceInit); + /** + * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close) + */ + close(): void; + /** + * Returns the URL providing the event stream. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url) + */ + get url(): string; + /** + * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials) + */ + get withCredentials(): boolean; + /** + * Returns the state of this EventSource object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState) + */ + get readyState(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + get onopen(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + set onopen(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + get onmessage(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + set onmessage(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + get onerror(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + set onerror(value: any | null); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSED: number; + static from(stream: ReadableStream): EventSource; +} +interface EventSourceEventSourceInit { + withCredentials?: boolean; + fetcher?: Fetcher; +} +interface Container { + get running(): boolean; + start(options?: ContainerStartupOptions): void; + monitor(): Promise; + destroy(error?: any): Promise; + signal(signo: number): void; + getTcpPort(port: number): Fetcher; +} +interface ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; +} +type AiImageClassificationInput = { + image: number[]; +}; +type AiImageClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiImageClassification { + inputs: AiImageClassificationInput; + postProcessedOutputs: AiImageClassificationOutput; +} +type AiImageToTextInput = { + image: number[]; + prompt?: string; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageToText { + inputs: AiImageToTextInput; + postProcessedOutputs: AiImageToTextOutput; +} +type AiObjectDetectionInput = { + image: number[]; +}; +type AiObjectDetectionOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiObjectDetection { + inputs: AiObjectDetectionInput; + postProcessedOutputs: AiObjectDetectionOutput; +} +type AiSentenceSimilarityInput = { + source: string; + sentences: string[]; +}; +type AiSentenceSimilarityOutput = number[]; +declare abstract class BaseAiSentenceSimilarity { + inputs: AiSentenceSimilarityInput; + postProcessedOutputs: AiSentenceSimilarityOutput; +} +type AiAutomaticSpeechRecognitionInput = { + audio: number[]; +}; +type AiAutomaticSpeechRecognitionOutput = { + text?: string; + words?: { + word: string; + start: number; + end: number; + }[]; + vtt?: string; +}; +declare abstract class BaseAiAutomaticSpeechRecognition { + inputs: AiAutomaticSpeechRecognitionInput; + postProcessedOutputs: AiAutomaticSpeechRecognitionOutput; +} +type AiSummarizationInput = { + input_text: string; + max_length?: number; +}; +type AiSummarizationOutput = { + summary: string; +}; +declare abstract class BaseAiSummarization { + inputs: AiSummarizationInput; + postProcessedOutputs: AiSummarizationOutput; +} +type AiTextClassificationInput = { + text: string; +}; +type AiTextClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiTextClassification { + inputs: AiTextClassificationInput; + postProcessedOutputs: AiTextClassificationOutput; +} +type AiTextEmbeddingsInput = { + text: string | string[]; +}; +type AiTextEmbeddingsOutput = { + shape: number[]; + data: number[][]; +}; +declare abstract class BaseAiTextEmbeddings { + inputs: AiTextEmbeddingsInput; + postProcessedOutputs: AiTextEmbeddingsOutput; +} +type RoleScopedChatInput = { + role: "user" | "assistant" | "system" | "tool" | (string & NonNullable); + content: string; + name?: string; +}; +type AiTextGenerationToolLegacyInput = { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; +}; +type AiTextGenerationToolInput = { + type: "function" | (string & NonNullable); + function: { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; + }; +}; +type AiTextGenerationFunctionsInput = { + name: string; + code: string; +}; +type AiTextGenerationResponseFormat = { + type: string; + json_schema?: any; +}; +type AiTextGenerationInput = { + prompt?: string; + raw?: boolean; + stream?: boolean; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + messages?: RoleScopedChatInput[]; + response_format?: AiTextGenerationResponseFormat; + tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable); + functions?: AiTextGenerationFunctionsInput[]; +}; +type AiTextGenerationOutput = { + response?: string; + tool_calls?: { + name: string; + arguments: unknown; + }[]; +} | ReadableStream; +declare abstract class BaseAiTextGeneration { + inputs: AiTextGenerationInput; + postProcessedOutputs: AiTextGenerationOutput; +} +type AiTextToSpeechInput = { + prompt: string; + lang?: string; +}; +type AiTextToSpeechOutput = Uint8Array | { + audio: string; +}; +declare abstract class BaseAiTextToSpeech { + inputs: AiTextToSpeechInput; + postProcessedOutputs: AiTextToSpeechOutput; +} +type AiTextToImageInput = { + prompt: string; + negative_prompt?: string; + height?: number; + width?: number; + image?: number[]; + image_b64?: string; + mask?: number[]; + num_steps?: number; + strength?: number; + guidance?: number; + seed?: number; +}; +type AiTextToImageOutput = ReadableStream; +declare abstract class BaseAiTextToImage { + inputs: AiTextToImageInput; + postProcessedOutputs: AiTextToImageOutput; +} +type AiTranslationInput = { + text: string; + target_lang: string; + source_lang?: string; +}; +type AiTranslationOutput = { + translated_text?: string; +}; +declare abstract class BaseAiTranslation { + inputs: AiTranslationInput; + postProcessedOutputs: AiTranslationOutput; +} +type Ai_Cf_Openai_Whisper_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper { + inputs: Ai_Cf_Openai_Whisper_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Output; +} +type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | { + /** + * The input text prompt for the model to generate a response. + */ + prompt?: string; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + image: number[] | (string & NonNullable); + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; +}; +interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output { + description?: string; +} +declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M { + inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input; + postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output; +} +type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Tiny_En_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En { + inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input { + /** + * Base64 encoded value of the audio data. + */ + audio: string; + /** + * Supported tasks are 'translate' or 'transcribe'. + */ + task?: string; + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * Preprocess the audio with a voice activity detection model. + */ + vad_filter?: string; + /** + * A text prompt to help provide context to the model on the contents of the audio. + */ + initial_prompt?: string; + /** + * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result. + */ + prefix?: string; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output { + transcription_info?: { + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1. + */ + language_probability?: number; + /** + * The total duration of the original audio file, in seconds. + */ + duration?: number; + /** + * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds. + */ + duration_after_vad?: number; + }; + /** + * The complete transcription of the audio. + */ + text: string; + /** + * The total number of words in the transcription. + */ + word_count?: number; + segments?: { + /** + * The starting time of the segment within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the segment within the audio, in seconds. + */ + end?: number; + /** + * The transcription of the segment. + */ + text?: string; + /** + * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs. + */ + temperature?: number; + /** + * The average log probability of the predictions for the words in this segment, indicating overall confidence. + */ + avg_logprob?: number; + /** + * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process. + */ + compression_ratio?: number; + /** + * The probability that the segment contains no speech, represented as a decimal between 0 and 1. + */ + no_speech_prob?: number; + words?: { + /** + * The individual word transcribed from the audio. + */ + word?: string; + /** + * The starting time of the word within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the word within the audio, in seconds. + */ + end?: number; + }[]; + }[]; + /** + * The transcription in WebVTT format, which includes timing and text information for use in subtitles. + */ + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo { + inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input { + /** + * A text description of the image you want to generate. + */ + prompt: string; + /** + * The number of diffusion steps; higher values can improve quality but take longer. + */ + steps?: number; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output { + /** + * The generated image in Base64 format. + */ + image?: string; +} +declare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell { + inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input; + postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages; +interface Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + image?: number[] | (string & NonNullable); + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; +} +interface Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + image?: number[] | string; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * If true, the response will be streamed back incrementally. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = { + /** + * The generated text response from the model + */ + response?: string; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | ReadableStream; +declare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct { + inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Input { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender must alternate between 'user' and 'assistant'. + */ + role: "user" | "assistant"; + /** + * The content of the message as a string. + */ + content: string; + }[]; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Dictate the output format of the generated response. + */ + response_format?: { + /** + * Set to json_object to process and output generated text as JSON. + */ + type?: string; + }; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Output { + response?: string | { + /** + * Whether the conversation is safe or not. + */ + safe?: boolean; + /** + * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe. + */ + categories?: string[]; + }; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; +} +declare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B { + inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output; +} +interface AiModels { + "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; + "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-inpainting": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-img2img": BaseAiTextToImage; + "@cf/lykon/dreamshaper-8-lcm": BaseAiTextToImage; + "@cf/bytedance/stable-diffusion-xl-lightning": BaseAiTextToImage; + "@cf/baai/bge-base-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-small-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-large-en-v1.5": BaseAiTextEmbeddings; + "@cf/microsoft/resnet-50": BaseAiImageClassification; + "@cf/facebook/detr-resnet-50": BaseAiObjectDetection; + "@cf/meta/llama-2-7b-chat-int8": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.1": BaseAiTextGeneration; + "@cf/meta/llama-2-7b-chat-fp16": BaseAiTextGeneration; + "@hf/thebloke/llama-2-13b-chat-awq": BaseAiTextGeneration; + "@hf/thebloke/mistral-7b-instruct-v0.1-awq": BaseAiTextGeneration; + "@hf/thebloke/zephyr-7b-beta-awq": BaseAiTextGeneration; + "@hf/thebloke/openhermes-2.5-mistral-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/neural-chat-7b-v3-1-awq": BaseAiTextGeneration; + "@hf/thebloke/llamaguard-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-base-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-instruct-awq": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-math-7b-instruct": BaseAiTextGeneration; + "@cf/defog/sqlcoder-7b-2": BaseAiTextGeneration; + "@cf/openchat/openchat-3.5-0106": BaseAiTextGeneration; + "@cf/tiiuae/falcon-7b-instruct": BaseAiTextGeneration; + "@cf/thebloke/discolm-german-7b-v1-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-0.5b-chat": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-7b-chat-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-14b-chat-awq": BaseAiTextGeneration; + "@cf/tinyllama/tinyllama-1.1b-chat-v1.0": BaseAiTextGeneration; + "@cf/microsoft/phi-2": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-1.8b-chat": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.2-lora": BaseAiTextGeneration; + "@hf/nousresearch/hermes-2-pro-mistral-7b": BaseAiTextGeneration; + "@hf/nexusflow/starling-lm-7b-beta": BaseAiTextGeneration; + "@hf/google/gemma-7b-it": BaseAiTextGeneration; + "@cf/meta-llama/llama-2-7b-chat-hf-lora": BaseAiTextGeneration; + "@cf/google/gemma-2b-it-lora": BaseAiTextGeneration; + "@cf/google/gemma-7b-it-lora": BaseAiTextGeneration; + "@hf/mistral/mistral-7b-instruct-v0.2": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/fblgit/una-cybertron-7b-v2-bf16": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct-awq": BaseAiTextGeneration; + "@hf/meta-llama/meta-llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-fp8": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-awq": BaseAiTextGeneration; + "@cf/meta/llama-3.2-3b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.2-1b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": BaseAiTextGeneration; + "@cf/meta/m2m100-1.2b": BaseAiTranslation; + "@cf/facebook/bart-large-cnn": BaseAiSummarization; + "@cf/llava-hf/llava-1.5-7b-hf": BaseAiImageToText; + "@cf/openai/whisper": Base_Ai_Cf_Openai_Whisper; + "@cf/unum/uform-gen2-qwen-500m": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M; + "@cf/openai/whisper-tiny-en": Base_Ai_Cf_Openai_Whisper_Tiny_En; + "@cf/openai/whisper-large-v3-turbo": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo; + "@cf/black-forest-labs/flux-1-schnell": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell; + "@cf/meta/llama-3.2-11b-vision-instruct": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct; + "@cf/meta/llama-guard-3-8b": Base_Ai_Cf_Meta_Llama_Guard_3_8B; +} +type AiOptions = { + gateway?: GatewayOptions; + returnRawResponse?: boolean; + prefix?: string; + extraHeaders?: object; +}; +type ConversionResponse = { + name: string; + mimeType: string; + format: "markdown"; + tokens: number; + data: string; +}; +type AiModelsSearchParams = { + author?: string; + hide_experimental?: boolean; + page?: number; + per_page?: number; + search?: string; + source?: number; + task?: string; +}; +type AiModelsSearchObject = { + id: string; + source: number; + name: string; + description: string; + task: { + id: string; + name: string; + description: string; + }; + tags: string[]; + properties: { + property_id: string; + value: string; + }[]; +}; +interface InferenceUpstreamError extends Error { +} +interface AiInternalError extends Error { +} +type AiModelListType = Record; +declare abstract class Ai { + aiGatewayLogId: string | null; + gateway(gatewayId: string): AiGateway; + autorag(autoragId: string): AutoRAG; + run(model: Name, inputs: AiModelList[Name]["inputs"], options?: Options): Promise; + public models(params?: AiModelsSearchParams): Promise; + public toMarkdown(files: { + name: string; + blob: Blob; + }[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + public toMarkdown(files: { + name: string; + blob: Blob; + }, options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; +} +type GatewayOptions = { + id: string; + cacheKey?: string; + cacheTtl?: number; + skipCache?: boolean; + metadata?: Record; + collectLog?: boolean; +}; +type AiGatewayPatchLog = { + score?: number | null; + feedback?: -1 | 1 | null; + metadata?: Record | null; +}; +type AiGatewayLog = { + id: string; + provider: string; + model: string; + model_type?: string; + path: string; + duration: number; + request_type?: string; + request_content_type?: string; + status_code: number; + response_content_type?: string; + success: boolean; + cached: boolean; + tokens_in?: number; + tokens_out?: number; + metadata?: Record; + step?: number; + cost?: number; + custom_cost?: boolean; + request_size: number; + request_head?: string; + request_head_complete: boolean; + response_size: number; + response_head?: string; + response_head_complete: boolean; + created_at: Date; +}; +type AIGatewayProviders = "workers-ai" | "anthropic" | "aws-bedrock" | "azure-openai" | "google-vertex-ai" | "huggingface" | "openai" | "perplexity-ai" | "replicate" | "groq" | "cohere" | "google-ai-studio" | "mistral" | "grok" | "openrouter" | "deepseek" | "cerebras" | "cartesia" | "elevenlabs" | "adobe-firefly"; +type AIGatewayHeaders = { + "cf-aig-metadata": Record | string; + "cf-aig-custom-cost": { + per_token_in?: number; + per_token_out?: number; + } | { + total_cost?: number; + } | string; + "cf-aig-cache-ttl": number | string; + "cf-aig-skip-cache": boolean | string; + "cf-aig-cache-key": string; + "cf-aig-collect-log": boolean | string; + Authorization: string; + "Content-Type": string; + [key: string]: string | number | boolean | object; +}; +type AIGatewayUniversalRequest = { + provider: AIGatewayProviders | string; + endpoint: string; + headers: Partial; + query: unknown; +}; +interface AiGatewayInternalError extends Error { +} +interface AiGatewayLogNotFound extends Error { +} +declare abstract class AiGateway { + patchLog(logId: string, data: AiGatewayPatchLog): Promise; + getLog(logId: string): Promise; + run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[]): Promise; + getUrl(provider?: AIGatewayProviders | string): Promise; +} +interface AutoRAGInternalError extends Error { +} +interface AutoRAGNotFoundError extends Error { +} +interface AutoRAGUnauthorizedError extends Error { +} +type AutoRagSearchRequest = { + query: string; + max_num_results?: number; + ranking_options?: { + ranker?: string; + score_threshold?: number; + }; + rewrite_query?: boolean; +}; +type AutoRagSearchResponse = { + object: "vector_store.search_results.page"; + search_query: string; + data: { + file_id: string; + filename: string; + score: number; + attributes: Record; + content: { + type: "text"; + text: string; + }[]; + }[]; + has_more: boolean; + next_page: string | null; +}; +type AutoRagAiSearchResponse = AutoRagSearchResponse & { + response: string; +}; +declare abstract class AutoRAG { + search(params: AutoRagSearchRequest): Promise; + aiSearch(params: AutoRagSearchRequest): Promise; +} +interface BasicImageTransformations { + /** + * Maximum width in image pixels. The value must be an integer. + */ + width?: number; + /** + * Maximum height in image pixels. The value must be an integer. + */ + height?: number; + /** + * Resizing mode as a string. It affects interpretation of width and height + * options: + * - scale-down: Similar to contain, but the image is never enlarged. If + * the image is larger than given width or height, it will be resized. + * Otherwise its original size will be kept. + * - contain: Resizes to maximum size that fits within the given width and + * height. If only a single dimension is given (e.g. only width), the + * image will be shrunk or enlarged to exactly match that dimension. + * Aspect ratio is always preserved. + * - cover: Resizes (shrinks or enlarges) to fill the entire area of width + * and height. If the image has an aspect ratio different from the ratio + * of width and height, it will be cropped to fit. + * - crop: The image will be shrunk and cropped to fit within the area + * specified by width and height. The image will not be enlarged. For images + * smaller than the given dimensions it's the same as scale-down. For + * images larger than the given dimensions, it's the same as cover. + * See also trim. + * - pad: Resizes to the maximum size that fits within the given width and + * height, and then fills the remaining area with a background color + * (white by default). Use of this mode is not recommended, as the same + * effect can be more efficiently achieved with the contain mode and the + * CSS object-fit: contain property. + */ + fit?: "scale-down" | "contain" | "cover" | "crop" | "pad"; + /** + * When cropping with fit: "cover", this defines the side or point that should + * be left uncropped. The value is either a string + * "left", "right", "top", "bottom", "auto", or "center" (the default), + * or an object {x, y} containing focal point coordinates in the original + * image expressed as fractions ranging from 0.0 (top or left) to 1.0 + * (bottom or right), 0.5 being the center. {fit: "cover", gravity: "top"} will + * crop bottom or left and right sides as necessary, but won’t crop anything + * from the top. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to + * preserve as much as possible around a point at 20% of the height of the + * source image. + */ + gravity?: "left" | "right" | "top" | "bottom" | "center" | "auto" | BasicImageTransformationsGravityCoordinates; + /** + * Background color to add underneath the image. Applies only to images with + * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…), + * hsl(…), etc.) + */ + background?: string; + /** + * Number of degrees (90, 180, 270) to rotate the image by. width and height + * options refer to axes after rotation. + */ + rotate?: 0 | 90 | 180 | 270 | 360; +} +interface BasicImageTransformationsGravityCoordinates { + x: number; + y: number; +} +/** + * In addition to the properties you can set in the RequestInit dict + * that you pass as an argument to the Request constructor, you can + * set certain properties of a `cf` object to control how Cloudflare + * features are applied to that new Request. + * + * Note: Currently, these properties cannot be tested in the + * playground. + */ +interface RequestInitCfProperties extends Record { + cacheEverything?: boolean; + /** + * A request's cache key is what determines if two requests are + * "the same" for caching purposes. If a request has the same cache key + * as some previous request, then we can serve the same cached response for + * both. (e.g. 'some-key') + * + * Only available for Enterprise customers. + */ + cacheKey?: string; + /** + * This allows you to append additional Cache-Tag response headers + * to the origin response without modifications to the origin server. + * This will allow for greater control over the Purge by Cache Tag feature + * utilizing changes only in the Workers process. + */ + cacheTags?: string[]; + /** + * Force response to be cached for a given number of seconds. (e.g. 300) + */ + cacheTtl?: number; + /** + * Force response to be cached for a given number of seconds based on the Origin status code. + * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 }) + */ + cacheTtlByStatus?: Record; + scrapeShield?: boolean; + apps?: boolean; + image?: RequestInitCfPropertiesImage; + minify?: RequestInitCfPropertiesImageMinify; + mirage?: boolean; + polish?: "lossy" | "lossless" | "off"; + r2?: RequestInitCfPropertiesR2; + /** + * Redirects the request to an alternate origin server. You can use this, + * for example, to implement load balancing across several origins. + * (e.g.us-east.example.com) + * + * Note - For security reasons, the hostname set in resolveOverride must + * be proxied on the same Cloudflare zone of the incoming request. + * Otherwise, the setting is ignored. CNAME hosts are allowed, so to + * resolve to a host under a different domain or a DNS only domain first + * declare a CNAME record within your own zone’s DNS mapping to the + * external hostname, set proxy on Cloudflare, then set resolveOverride + * to point to that CNAME record. + */ + resolveOverride?: string; +} +interface RequestInitCfPropertiesImageDraw extends BasicImageTransformations { + /** + * Absolute URL of the image file to use for the drawing. It can be any of + * the supported file formats. For drawing of watermarks or non-rectangular + * overlays we recommend using PNG or WebP images. + */ + url: string; + /** + * Floating-point number between 0 (transparent) and 1 (opaque). + * For example, opacity: 0.5 makes overlay semitransparent. + */ + opacity?: number; + /** + * - If set to true, the overlay image will be tiled to cover the entire + * area. This is useful for stock-photo-like watermarks. + * - If set to "x", the overlay image will be tiled horizontally only + * (form a line). + * - If set to "y", the overlay image will be tiled vertically only + * (form a line). + */ + repeat?: true | "x" | "y"; + /** + * Position of the overlay image relative to a given edge. Each property is + * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10 + * positions left side of the overlay 10 pixels from the left edge of the + * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom + * of the background image. + * + * Setting both left & right, or both top & bottom is an error. + * + * If no position is specified, the image will be centered. + */ + top?: number; + left?: number; + bottom?: number; + right?: number; +} +interface RequestInitCfPropertiesImage extends BasicImageTransformations { + /** + * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it + * easier to specify higher-DPI sizes in . + */ + dpr?: number; + /** + * An object with four properties {left, top, right, bottom} that specify + * a number of pixels to cut off on each side. Allows removal of borders + * or cutting out a specific fragment of an image. Trimming is performed + * before resizing or rotation. Takes dpr into account. + */ + trim?: { + left?: number; + top?: number; + right?: number; + bottom?: number; + }; + /** + * Quality setting from 1-100 (useful values are in 60-90 range). Lower values + * make images look worse, but load faster. The default is 85. It applies only + * to JPEG and WebP images. It doesn’t have any effect on PNG. + */ + quality?: number; + /** + * Output format to generate. It can be: + * - avif: generate images in AVIF format. + * - webp: generate images in Google WebP format. Set quality to 100 to get + * the WebP-lossless format. + * - json: instead of generating an image, outputs information about the + * image, in JSON format. The JSON object will contain image size + * (before and after resizing), source image’s MIME type, file size, etc. + * - jpeg: generate images in JPEG format. + * - png: generate images in PNG format. + */ + format?: "avif" | "webp" | "json" | "jpeg" | "png"; + /** + * Whether to preserve animation frames from input files. Default is true. + * Setting it to false reduces animations to still images. This setting is + * recommended when enlarging images or processing arbitrary user content, + * because large GIF animations can weigh tens or even hundreds of megabytes. + * It is also useful to set anim:false when using format:"json" to get the + * response quicker without the number of frames. + */ + anim?: boolean; + /** + * What EXIF data should be preserved in the output image. Note that EXIF + * rotation and embedded color profiles are always applied ("baked in" into + * the image), and aren't affected by this option. Note that if the Polish + * feature is enabled, all metadata may have been removed already and this + * option may have no effect. + * - keep: Preserve most of EXIF metadata, including GPS location if there's + * any. + * - copyright: Only keep the copyright tag, and discard everything else. + * This is the default behavior for JPEG files. + * - none: Discard all invisible EXIF metadata. Currently WebP and PNG + * output formats always discard metadata. + */ + metadata?: "keep" | "copyright" | "none"; + /** + * Strength of sharpening filter to apply to the image. Floating-point + * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a + * recommended value for downscaled images. + */ + sharpen?: number; + /** + * Radius of a blur filter (approximate gaussian). Maximum supported radius + * is 250. + */ + blur?: number; + /** + * Overlays are drawn in the order they appear in the array (last array + * entry is the topmost layer). + */ + draw?: RequestInitCfPropertiesImageDraw[]; + /** + * Fetching image from authenticated origin. Setting this property will + * pass authentication headers (Authorization, Cookie, etc.) through to + * the origin. + */ + "origin-auth"?: "share-publicly"; + /** + * Adds a border around the image. The border is added after resizing. Border + * width takes dpr into account, and can be specified either using a single + * width property, or individually for each side. + */ + border?: { + color: string; + width: number; + } | { + color: string; + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Increase brightness by a factor. A value of 1.0 equals no change, a value + * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. + * 0 is ignored. + */ + brightness?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + contrast?: number; + /** + * Increase exposure by a factor. A value of 1.0 equals no change, a value of + * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored. + */ + gamma?: number; + /** + * Slightly reduces latency on a cache miss by selecting a + * quickest-to-compress file format, at a cost of increased file size and + * lower image quality. It will usually override the format option and choose + * JPEG over WebP or AVIF. We do not recommend using this option, except in + * unusual circumstances like resizing uncacheable dynamically-generated + * images. + */ + compression?: "fast"; +} +interface RequestInitCfPropertiesImageMinify { + javascript?: boolean; + css?: boolean; + html?: boolean; +} +interface RequestInitCfPropertiesR2 { + /** + * Colo id of bucket that an object is stored in + */ + bucketColoId?: number; +} +/** + * Request metadata provided by Cloudflare's edge. + */ +type IncomingRequestCfProperties = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield; +interface IncomingRequestCfPropertiesBase extends Record { + /** + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 + */ + asn: number; + /** + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" + */ + asOrganization: string; + /** + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" + */ + clientAcceptEncoding?: string; + /** + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 + */ + clientTcpRtt?: number; + /** + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" + */ + colo: string; + /** + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 + */ + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** + * The HTTP Protocol the request used. + * + * @example "HTTP/2" + */ + httpProtocol: string; + /** + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" + */ + requestPriority: string; + /** + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" + */ + tlsVersion: string; + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ + tlsCipher: string; + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; +} +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human). + * + * @example 54 + */ + score: number; + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ + verifiedBot: boolean; + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; + /** + * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request). + */ + detectionIds: number[]; +} +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} +interface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; +} +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} +/** + * Geographic data about the request's origin. + */ +interface IncomingRequestCfPropertiesGeographicInformation { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * The country code `"T1"` is used for requests originating on TOR. + * + * @example "GB" + */ + country?: Iso3166Alpha2Code | "T1"; + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent?: ContinentCode; + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; +} +/** Data about the incoming request's TLS certificate */ +interface IncomingRequestCfPropertiesTLSClientAuth { + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDN: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDN: string; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDNRFC2253: string; + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ + certSerial: string; + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certSKI: string; + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; +} +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = +/** Authentication succeeded */ +"SUCCESS" +/** No certificate was presented */ + | "NONE" +/** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" +/** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" +/** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" +/** Failed because the certificate is expired */ + | "FAILED:certificate has expired" +/** Failed for another unspecified reason */ + | "FAILED"; +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */ +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; +type CfProperties = IncomingRequestCfProperties | RequestInitCfProperties; +interface D1Meta { + duration: number; + size_after: number; + rows_read: number; + rows_written: number; + last_row_id: number; + changed_db: boolean; + changes: number; +} +interface D1Response { + success: true; + meta: D1Meta & Record; + error?: never; +} +type D1Result = D1Response & { + results: T[]; +}; +interface D1ExecResult { + count: number; + duration: number; +} +type D1SessionConstraint = +// Indicates that the first query should go to the primary, and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). +"first-primary" +// Indicates that the first query can go anywhere (primary or replica), and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). + | "first-unconstrained"; +type D1SessionBookmark = string; +declare abstract class D1Database { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + exec(query: string): Promise; + /** + * Creates a new D1 Session anchored at the given constraint or the bookmark. + * All queries executed using the created session will have sequential consistency, + * meaning that all writes done through the session will be visible in subsequent reads. + * + * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session. + */ + withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession; + /** + * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases. + */ + dump(): Promise; +} +declare abstract class D1DatabaseSession { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + /** + * @returns The latest session bookmark across all executed queries on the session. + * If no query has been executed yet, `null` is returned. + */ + getBookmark(): D1SessionBookmark | null; +} +declare abstract class D1PreparedStatement { + bind(...values: unknown[]): D1PreparedStatement; + first(colName: string): Promise; + first>(): Promise; + run>(): Promise>; + all>(): Promise>; + raw(options: { + columnNames: true; + }): Promise<[ + string[], + ...T[] + ]>; + raw(options?: { + columnNames?: false; + }): Promise; +} +// `Disposable` was added to TypeScript's standard lib types in version 5.2. +// To support older TypeScript versions, define an empty `Disposable` interface. +// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2, +// but this will ensure type checking on older versions still passes. +// TypeScript's interface merging will ensure our empty interface is effectively +// ignored when `Disposable` is included in the standard lib. +interface Disposable { +} +/** + * An email message that can be sent from a Worker. + */ +interface EmailMessage { + /** + * Envelope From attribute of the email message. + */ + readonly from: string; + /** + * Envelope To attribute of the email message. + */ + readonly to: string; +} +/** + * An email message that is sent to a consumer Worker and can be rejected/forwarded. + */ +interface ForwardableEmailMessage extends EmailMessage { + /** + * Stream of the email message content. + */ + readonly raw: ReadableStream; + /** + * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + */ + readonly headers: Headers; + /** + * Size of the email message content. + */ + readonly rawSize: number; + /** + * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason. + * @param reason The reject reason. + * @returns void + */ + setReject(reason: string): void; + /** + * Forward this email message to a verified destination address of the account. + * @param rcptTo Verified destination address. + * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + * @returns A promise that resolves when the email message is forwarded. + */ + forward(rcptTo: string, headers?: Headers): Promise; + /** + * Reply to the sender of this email message with a new EmailMessage object. + * @param message The reply message. + * @returns A promise that resolves when the email message is replied. + */ + reply(message: EmailMessage): Promise; +} +/** + * A binding that allows a Worker to send email messages. + */ +interface SendEmail { + send(message: EmailMessage): Promise; +} +declare abstract class EmailEvent extends ExtendableEvent { + readonly message: ForwardableEmailMessage; +} +declare type EmailExportedHandler = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise; +declare module "cloudflare:email" { + let _EmailMessage: { + prototype: EmailMessage; + new (from: string, to: string, raw: ReadableStream | string): EmailMessage; + }; + export { _EmailMessage as EmailMessage }; +} +interface Hyperdrive { + /** + * Connect directly to Hyperdrive as if it's your database, returning a TCP socket. + * + * Calling this method returns an idential socket to if you call + * `connect("host:port")` using the `host` and `port` fields from this object. + * Pick whichever approach works better with your preferred DB client library. + * + * Note that this socket is not yet authenticated -- it's expected that your + * code (or preferably, the client library of your choice) will authenticate + * using the information in this class's readonly fields. + */ + connect(): Socket; + /** + * A valid DB connection string that can be passed straight into the typical + * client library/driver/ORM. This will typically be the easiest way to use + * Hyperdrive. + */ + readonly connectionString: string; + /* + * A randomly generated hostname that is only valid within the context of the + * currently running Worker which, when passed into `connect()` function from + * the "cloudflare:sockets" module, will connect to the Hyperdrive instance + * for your database. + */ + readonly host: string; + /* + * The port that must be paired the the host field when connecting. + */ + readonly port: number; + /* + * The username to use when authenticating to your database via Hyperdrive. + * Unlike the host and password, this will be the same every time + */ + readonly user: string; + /* + * The randomly generated password to use when authenticating to your + * database via Hyperdrive. Like the host field, this password is only valid + * within the context of the currently running Worker instance from which + * it's read. + */ + readonly password: string; + /* + * The name of the database to connect to. + */ + readonly database: string; +} +// Copyright (c) 2024 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +type ImageInfoResponse = { + format: 'image/svg+xml'; +} | { + format: string; + fileSize: number; + width: number; + height: number; +}; +type ImageTransform = { + fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop'; + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | 'face' | { + x?: number; + y?: number; + mode: 'remainder' | 'box-center'; + }; + trim?: { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; + width?: number; + height?: number; + background?: string; + rotate?: number; + sharpen?: number; + blur?: number; + contrast?: number; + brightness?: number; + gamma?: number; + border?: { + color?: string; + width?: number; + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + zoom?: number; +}; +type ImageDrawOptions = { + opacity?: number; + repeat?: boolean | string; + top?: number; + left?: number; + bottom?: number; + right?: number; +}; +type ImageOutputOptions = { + format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba'; + quality?: number; + background?: string; +}; +interface ImagesBinding { + /** + * Get image metadata (type, width and height) + * @throws {@link ImagesError} with code 9412 if input is not an image + * @param stream The image bytes + */ + info(stream: ReadableStream): Promise; + /** + * Begin applying a series of transformations to an image + * @param stream The image bytes + * @returns A transform handle + */ + input(stream: ReadableStream): ImageTransformer; +} +interface ImageTransformer { + /** + * Apply transform next, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param transform + */ + transform(transform: ImageTransform): ImageTransformer; + /** + * Draw an image on this transformer, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param image The image (or transformer that will give the image) to draw + * @param options The options configuring how to draw the image + */ + draw(image: ReadableStream | ImageTransformer, options?: ImageDrawOptions): ImageTransformer; + /** + * Retrieve the image that results from applying the transforms to the + * provided input + * @param options Options that apply to the output e.g. output format + */ + output(options: ImageOutputOptions): Promise; +} +interface ImageTransformationResult { + /** + * The image as a response, ready to store in cache or return to users + */ + response(): Response; + /** + * The content type of the returned image + */ + contentType(): string; + /** + * The bytes of the response + */ + image(): ReadableStream; +} +interface ImagesError extends Error { + readonly code: number; + readonly message: string; + readonly stack?: string; +} +type Params

= Record; +type EventContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; +}; +type PagesFunction = Record> = (context: EventContext) => Response | Promise; +type EventPluginContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; + pluginArgs: PluginArgs; +}; +type PagesPluginFunction = Record, PluginArgs = unknown> = (context: EventPluginContext) => Response | Promise; +declare module "assets:*" { + export const onRequest: PagesFunction; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +declare module "cloudflare:pipelines" { + export abstract class PipelineTransformationEntrypoint { + /** + * run recieves an array of PipelineRecord which can be + * mutated and returned to the pipeline + * @param records Incoming records from the pipeline to be transformed + * @param metadata Information about the specific pipeline calling the transformation entrypoint + * @returns A promise containing the transformed PipelineRecord array + */ + protected env: Env; + protected ctx: ExecutionContext; + constructor(ctx: ExecutionContext, env: Env); + public run(records: I[], metadata: PipelineBatchMetadata): Promise; + } + export type PipelineRecord = Record; + export type PipelineBatchMetadata = { + pipelineId: string; + pipelineName: string; + }; + export interface Pipeline { + /** + * The Pipeline interface represents the type of a binding to a Pipeline + * + * @param records The records to send to the pipeline + */ + send(records: T[]): Promise; + } +} +// PubSubMessage represents an incoming PubSub message. +// The message includes metadata about the broker, the client, and the payload +// itself. +// https://developers.cloudflare.com/pub-sub/ +interface PubSubMessage { + // Message ID + readonly mid: number; + // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT + readonly broker: string; + // The MQTT topic the message was sent on. + readonly topic: string; + // The client ID of the client that published this message. + readonly clientId: string; + // The unique identifier (JWT ID) used by the client to authenticate, if token + // auth was used. + readonly jti?: string; + // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker + // received the message from the client. + readonly receivedAt: number; + // An (optional) string with the MIME type of the payload, if set by the + // client. + readonly contentType: string; + // Set to 1 when the payload is a UTF-8 string + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063 + readonly payloadFormatIndicator: number; + // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays. + // You can use payloadFormatIndicator to inspect this before decoding. + payload: string | Uint8Array; +} +// JsonWebKey extended by kid parameter +interface JsonWebKeyWithKid extends JsonWebKey { + // Key Identifier of the JWK + readonly kid: string; +} +interface RateLimitOptions { + key: string; +} +interface RateLimitOutcome { + success: boolean; +} +interface RateLimit { + /** + * Rate limit a request based on the provided options. + * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ + * @returns A promise that resolves with the outcome of the rate limit. + */ + limit(options: RateLimitOptions): Promise; +} +// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need +// to referenced by `Fetcher`. This is included in the "importable" version of the types which +// strips all `module` blocks. +declare namespace Rpc { + // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s. + // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`. + // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to + // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape) + export const __RPC_STUB_BRAND: "__RPC_STUB_BRAND"; + export const __RPC_TARGET_BRAND: "__RPC_TARGET_BRAND"; + export const __WORKER_ENTRYPOINT_BRAND: "__WORKER_ENTRYPOINT_BRAND"; + export const __DURABLE_OBJECT_BRAND: "__DURABLE_OBJECT_BRAND"; + export const __WORKFLOW_ENTRYPOINT_BRAND: "__WORKFLOW_ENTRYPOINT_BRAND"; + export interface RpcTargetBranded { + [__RPC_TARGET_BRAND]: never; + } + export interface WorkerEntrypointBranded { + [__WORKER_ENTRYPOINT_BRAND]: never; + } + export interface DurableObjectBranded { + [__DURABLE_OBJECT_BRAND]: never; + } + export interface WorkflowEntrypointBranded { + [__WORKFLOW_ENTRYPOINT_BRAND]: never; + } + export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded; + // Types that can be used through `Stub`s + export type Stubable = RpcTargetBranded | ((...args: any[]) => any); + // Types that can be passed over RPC + // The reason for using a generic type here is to build a serializable subset of structured + // cloneable composite types. This allows types defined with the "interface" keyword to pass the + // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. + type Serializable = + // Structured cloneables + void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp + // Structured cloneable composites + | Map ? Serializable : never, T extends Map ? Serializable : never> | Set ? Serializable : never> | ReadonlyArray ? Serializable : never> | { + [K in keyof T]: K extends number | string ? Serializable : never; + } + // Special types + | ReadableStream | WritableStream | Request | Response | Headers | Stub + // Serialized as stubs, see `Stubify` + | Stubable; + // Base type for all RPC stubs, including common memory management methods. + // `T` is used as a marker type for unwrapping `Stub`s later. + interface StubBase extends Disposable { + [__RPC_STUB_BRAND]: T; + dup(): this; + } + export type Stub = Provider & StubBase; + // Recursively rewrite all `Stubable` types with `Stub`s + // prettier-ignore + type Stubify = T extends Stubable ? Stub : T extends Map ? Map, Stubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends { + [key: string | number]: any; + } ? { + [K in keyof T]: Stubify; + } : T; + // Recursively rewrite all `Stub`s with the corresponding `T`s. + // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies: + // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`. + // prettier-ignore + type Unstubify = T extends StubBase ? V : T extends Map ? Map, Unstubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends { + [key: string | number]: unknown; + } ? { + [K in keyof T]: Unstubify; + } : T; + type UnstubifyAll = { + [I in keyof A]: Unstubify; + }; + // Utility type for adding `Provider`/`Disposable`s to `object` types only. + // Note `unknown & T` is equivalent to `T`. + type MaybeProvider = T extends object ? Provider : unknown; + type MaybeDisposable = T extends object ? Disposable : unknown; + // Type for method return or property on an RPC interface. + // - Stubable types are replaced by stubs. + // - Serializable types are passed by value, with stubable types replaced by stubs + // and a top-level `Disposer`. + // Everything else can't be passed over PRC. + // Technically, we use custom thenables here, but they quack like `Promise`s. + // Intersecting with `(Maybe)Provider` allows pipelining. + // prettier-ignore + type Result = R extends Stubable ? Promise> & Provider : R extends Serializable ? Promise & MaybeDisposable> & MaybeProvider : never; + // Type for method or property on an RPC interface. + // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s. + // Unwrapping `Stub`s allows calling with `Stubable` arguments. + // For properties, rewrite types to be `Result`s. + // In each case, unwrap `Promise`s. + type MethodOrProperty = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll

) => Result> : Result>; + // Type for the callable part of an `Provider` if `T` is callable. + // This is intersected with methods/properties. + type MaybeCallableProvider = T extends (...args: any[]) => any ? MethodOrProperty : unknown; + // Base type for all other types providing RPC-like interfaces. + // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types. + // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC. + export type Provider = MaybeCallableProvider & { + [K in Exclude>]: MethodOrProperty; + }; +} +declare namespace Cloudflare { + interface Env { + } +} +declare module "cloudflare:workers" { + export type RpcStub = Rpc.Stub; + export const RpcStub: { + new (value: T): Rpc.Stub; + }; + export abstract class RpcTarget implements Rpc.RpcTargetBranded { + [Rpc.__RPC_TARGET_BRAND]: never; + } + // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC + export abstract class WorkerEntrypoint implements Rpc.WorkerEntrypointBranded { + [Rpc.__WORKER_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + fetch?(request: Request): Response | Promise; + tail?(events: TraceItem[]): void | Promise; + trace?(traces: TraceItem[]): void | Promise; + scheduled?(controller: ScheduledController): void | Promise; + queue?(batch: MessageBatch): void | Promise; + test?(controller: TestController): void | Promise; + } + export abstract class DurableObject implements Rpc.DurableObjectBranded { + [Rpc.__DURABLE_OBJECT_BRAND]: never; + protected ctx: DurableObjectState; + protected env: Env; + constructor(ctx: DurableObjectState, env: Env); + fetch?(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; + } + export type WorkflowDurationLabel = "second" | "minute" | "hour" | "day" | "week" | "month" | "year"; + export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${"s" | ""}` | number; + export type WorkflowDelayDuration = WorkflowSleepDuration; + export type WorkflowTimeoutDuration = WorkflowSleepDuration; + export type WorkflowBackoff = "constant" | "linear" | "exponential"; + export type WorkflowStepConfig = { + retries?: { + limit: number; + delay: WorkflowDelayDuration | number; + backoff?: WorkflowBackoff; + }; + timeout?: WorkflowTimeoutDuration | number; + }; + export type WorkflowEvent = { + payload: Readonly; + timestamp: Date; + instanceId: string; + }; + export abstract class WorkflowStep { + do>(name: string, callback: () => Promise): Promise; + do>(name: string, config: WorkflowStepConfig, callback: () => Promise): Promise; + sleep: (name: string, duration: WorkflowSleepDuration) => Promise; + sleepUntil: (name: string, timestamp: Date | number) => Promise; + } + export abstract class WorkflowEntrypoint | unknown = unknown> implements Rpc.WorkflowEntrypointBranded { + [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + run(event: Readonly>, step: WorkflowStep): Promise; + } + export const env: Cloudflare.Env; +} +declare module "cloudflare:sockets" { + function _connect(address: string | SocketAddress, options?: SocketOptions): Socket; + export { _connect as connect }; +} +declare namespace TailStream { + interface Header { + readonly name: string; + readonly value: string; + } + interface FetchEventInfo { + readonly type: "fetch"; + readonly method: string; + readonly url: string; + readonly cfJson: string; + readonly headers: Header[]; + } + interface JsRpcEventInfo { + readonly type: "jsrpc"; + readonly methodName: string; + } + interface ScheduledEventInfo { + readonly type: "scheduled"; + readonly scheduledTime: Date; + readonly cron: string; + } + interface AlarmEventInfo { + readonly type: "alarm"; + readonly scheduledTime: Date; + } + interface QueueEventInfo { + readonly type: "queue"; + readonly queueName: string; + readonly batchSize: number; + } + interface EmailEventInfo { + readonly type: "email"; + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; + } + interface TraceEventInfo { + readonly type: "trace"; + readonly traces: (string | null)[]; + } + interface HibernatableWebSocketEventInfoMessage { + readonly type: "message"; + } + interface HibernatableWebSocketEventInfoError { + readonly type: "error"; + } + interface HibernatableWebSocketEventInfoClose { + readonly type: "close"; + readonly code: number; + readonly wasClean: boolean; + } + interface HibernatableWebSocketEventInfo { + readonly type: "hibernatableWebSocket"; + readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage; + } + interface Resume { + readonly type: "resume"; + readonly attachment?: any; + } + interface CustomEventInfo { + readonly type: "custom"; + } + interface FetchResponseInfo { + readonly type: "fetch"; + readonly statusCode: number; + } + type EventOutcome = "ok" | "canceled" | "exception" | "unknown" | "killSwitch" | "daemonDown" | "exceededCpu" | "exceededMemory" | "loadShed" | "responseStreamDisconnected" | "scriptNotFound"; + interface ScriptVersion { + readonly id: string; + readonly tag?: string; + readonly message?: string; + } + interface Trigger { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Onset { + readonly type: "onset"; + readonly dispatchNamespace?: string; + readonly entrypoint?: string; + readonly scriptName?: string; + readonly scriptTags?: string[]; + readonly scriptVersion?: ScriptVersion; + readonly trigger?: Trigger; + readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | Resume | CustomEventInfo; + } + interface Outcome { + readonly type: "outcome"; + readonly outcome: EventOutcome; + readonly cpuTime: number; + readonly wallTime: number; + } + interface Hibernate { + readonly type: "hibernate"; + } + interface SpanOpen { + readonly type: "spanOpen"; + readonly op?: string; + readonly info?: FetchEventInfo | JsRpcEventInfo | Attribute[]; + } + interface SpanClose { + readonly type: "spanClose"; + readonly outcome: EventOutcome; + } + interface DiagnosticChannelEvent { + readonly type: "diagnosticChannel"; + readonly channel: string; + readonly message: any; + } + interface Exception { + readonly type: "exception"; + readonly name: string; + readonly message: string; + readonly stack?: string; + } + interface Log { + readonly type: "log"; + readonly level: "debug" | "error" | "info" | "log" | "warn"; + readonly message: string; + } + interface Return { + readonly type: "return"; + readonly info?: FetchResponseInfo | Attribute[]; + } + interface Link { + readonly type: "link"; + readonly label?: string; + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Attribute { + readonly type: "attribute"; + readonly name: string; + readonly value: string | string[] | boolean | boolean[] | number | number[]; + } + type Mark = DiagnosticChannelEvent | Exception | Log | Return | Link | Attribute[]; + interface TailEvent { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + readonly timestamp: Date; + readonly sequence: number; + readonly event: Onset | Outcome | Hibernate | SpanOpen | SpanClose | Mark; + } + type TailEventHandler = (event: TailEvent) => void | Promise; + type TailEventHandlerName = "onset" | "outcome" | "hibernate" | "spanOpen" | "spanClose" | "diagnosticChannel" | "exception" | "log" | "return" | "link" | "attribute"; + type TailEventHandlerObject = Record; + type TailEventHandlerType = TailEventHandler | TailEventHandlerObject; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +/** + * Data types supported for holding vector metadata. + */ +type VectorizeVectorMetadataValue = string | number | boolean | string[]; +/** + * Additional information to associate with a vector. + */ +type VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record; +type VectorFloatArray = Float32Array | Float64Array; +interface VectorizeError { + code?: number; + error: string; +} +/** + * Comparison logic/operation to use for metadata filtering. + * + * This list is expected to grow as support for more operations are released. + */ +type VectorizeVectorMetadataFilterOp = "$eq" | "$ne"; +/** + * Filter criteria for vector metadata used to limit the retrieved query result set. + */ +type VectorizeVectorMetadataFilter = { + [field: string]: Exclude | null | { + [Op in VectorizeVectorMetadataFilterOp]?: Exclude | null; + }; +}; +/** + * Supported distance metrics for an index. + * Distance metrics determine how other "similar" vectors are determined. + */ +type VectorizeDistanceMetric = "euclidean" | "cosine" | "dot-product"; +/** + * Metadata return levels for a Vectorize query. + * + * Default to "none". + * + * @property all Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data. + * @property indexed Return all metadata fields configured for indexing in the vector return set. This level of retrieval is "free" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings). + * @property none No indexed metadata will be returned. + */ +type VectorizeMetadataRetrievalLevel = "all" | "indexed" | "none"; +interface VectorizeQueryOptions { + topK?: number; + namespace?: string; + returnValues?: boolean; + returnMetadata?: boolean | VectorizeMetadataRetrievalLevel; + filter?: VectorizeVectorMetadataFilter; +} +/** + * Information about the configuration of an index. + */ +type VectorizeIndexConfig = { + dimensions: number; + metric: VectorizeDistanceMetric; +} | { + preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity +}; +/** + * Metadata about an existing index. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeIndexInfo} for its post-beta equivalent. + */ +interface VectorizeIndexDetails { + /** The unique ID of the index */ + readonly id: string; + /** The name of the index. */ + name: string; + /** (optional) A human readable description for the index. */ + description?: string; + /** The index configuration, including the dimension size and distance metric. */ + config: VectorizeIndexConfig; + /** The number of records containing vectors within the index. */ + vectorsCount: number; +} +/** + * Metadata about an existing index. + */ +interface VectorizeIndexInfo { + /** The number of records containing vectors within the index. */ + vectorCount: number; + /** Number of dimensions the index has been configured for. */ + dimensions: number; + /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */ + processedUpToDatetime: number; + /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */ + processedUpToMutation: number; +} +/** + * Represents a single vector value set along with its associated metadata. + */ +interface VectorizeVector { + /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */ + id: string; + /** The vector values */ + values: VectorFloatArray | number[]; + /** The namespace this vector belongs to. */ + namespace?: string; + /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */ + metadata?: Record; +} +/** + * Represents a matched vector for a query along with its score and (if specified) the matching vector information. + */ +type VectorizeMatch = Pick, "values"> & Omit & { + /** The score or rank for similarity, when returned as a result */ + score: number; +}; +/** + * A set of matching {@link VectorizeMatch} for a particular query. + */ +interface VectorizeMatches { + matches: VectorizeMatch[]; + count: number; +} +/** + * Results of an operation that performed a mutation on a set of vectors. + * Here, `ids` is a list of vectors that were successfully processed. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeAsyncMutation} for its post-beta equivalent. + */ +interface VectorizeVectorMutation { + /* List of ids of vectors that were successfully processed. */ + ids: string[]; + /* Total count of the number of processed vectors. */ + count: number; +} +/** + * Result type indicating a mutation on the Vectorize Index. + * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation. + */ +interface VectorizeAsyncMutation { + /** The unique identifier for the async mutation operation containing the changeset. */ + mutationId: string; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link Vectorize} for its new implementation. + */ +declare abstract class VectorizeIndex { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted). + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * Mutations in this version are async, returning a mutation id. + */ +declare abstract class Vectorize { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Use the provided vector-id to perform a similarity search across the index. + * @param vectorId Id for a vector in the index against which the index should be queried. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset. + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * The interface for "version_metadata" binding + * providing metadata about the Worker Version using this binding. + */ +type WorkerVersionMetadata = { + /** The ID of the Worker Version using this binding */ + id: string; + /** The tag of the Worker Version using this binding */ + tag: string; + /** The timestamp of when the Worker Version was uploaded */ + timestamp: string; +}; +interface DynamicDispatchLimits { + /** + * Limit CPU time in milliseconds. + */ + cpuMs?: number; + /** + * Limit number of subrequests. + */ + subRequests?: number; +} +interface DynamicDispatchOptions { + /** + * Limit resources of invoked Worker script. + */ + limits?: DynamicDispatchLimits; + /** + * Arguments for outbound Worker script, if configured. + */ + outbound?: { + [key: string]: any; + }; +} +interface DispatchNamespace { + /** + * @param name Name of the Worker script. + * @param args Arguments to Worker script. + * @param options Options for Dynamic Dispatch invocation. + * @returns A Fetcher object that allows you to send requests to the Worker script. + * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown. + */ + get(name: string, args?: { + [key: string]: any; + }, options?: DynamicDispatchOptions): Fetcher; +} +declare module "cloudflare:workflows" { + /** + * NonRetryableError allows for a user to throw a fatal error + * that makes a Workflow instance fail immediately without triggering a retry + */ + export class NonRetryableError extends Error { + public constructor(message: string, name?: string); + } +} +declare abstract class Workflow { + /** + * Get a handle to an existing instance of the Workflow. + * @param id Id for the instance of this Workflow + * @returns A promise that resolves with a handle for the Instance + */ + public get(id: string): Promise; + /** + * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown. + * @param options Options when creating an instance including id and params + * @returns A promise that resolves with a handle for the Instance + */ + public create(options?: WorkflowInstanceCreateOptions): Promise; + /** + * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. + * @param batch List of Options when creating an instance including name and params + * @returns A promise that resolves with a list of handles for the created instances. + */ + public createBatch(batch: WorkflowInstanceCreateOptions[]): Promise; +} +interface WorkflowInstanceCreateOptions { + /** + * An id for your Workflow instance. Must be unique within the Workflow. + */ + id?: string; + /** + * The event payload the Workflow instance is triggered with + */ + params?: PARAMS; +} +type InstanceStatus = { + status: "queued" // means that instance is waiting to be started (see concurrency limits) + | "running" | "paused" | "errored" | "terminated" // user terminated the instance while it was running + | "complete" | "waiting" // instance is hibernating and waiting for sleep or event to finish + | "waitingForPause" // instance is finishing the current work to pause + | "unknown"; + error?: string; + output?: object; +}; +interface WorkflowError { + code?: number; + message: string; +} +declare abstract class WorkflowInstance { + public id: string; + /** + * Pause the instance. + */ + public pause(): Promise; + /** + * Resume the instance. If it is already running, an error will be thrown. + */ + public resume(): Promise; + /** + * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. + */ + public terminate(): Promise; + /** + * Restart the instance. + */ + public restart(): Promise; + /** + * Returns the current status of the instance. + */ + public status(): Promise; +} diff --git a/packages/mcp-server/cloudflare-worker/wrangler.jsonc b/packages/mcp-server/cloudflare-worker/wrangler.jsonc new file mode 100644 index 00000000..a49004af --- /dev/null +++ b/packages/mcp-server/cloudflare-worker/wrangler.jsonc @@ -0,0 +1,35 @@ +/** + * For more details on how to configure Wrangler, refer to: + * https://developers.cloudflare.com/workers/wrangler/configuration/ + */ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "imagekit-nodejs-api-mcp-server", + "main": "src/index.ts", + "compatibility_date": "2025-03-10", + "compatibility_flags": ["nodejs_compat"], + "migrations": [ + { + "new_sqlite_classes": ["MyMCP"], + "tag": "v1" + } + ], + "durable_objects": { + "bindings": [ + { + "class_name": "MyMCP", + "name": "MCP_OBJECT" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "ae6fe7d7993146a9b8d54d87f73b0bdf" + } + ], + "observability": { + "enabled": true + }, + "assets": { "directory": "./static/", "binding": "ASSETS" } +} diff --git a/packages/mcp-server/jest.config.ts b/packages/mcp-server/jest.config.ts new file mode 100644 index 00000000..2744c3ba --- /dev/null +++ b/packages/mcp-server/jest.config.ts @@ -0,0 +1,17 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + preset: 'ts-jest/presets/default-esm', + testEnvironment: 'node', + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }], + }, + moduleNameMapper: { + '^imagekit-api-mcp$': '/src/index.ts', + '^imagekit-api-mcp/(.*)$': '/src/$1', + }, + modulePathIgnorePatterns: ['/dist/'], + testPathIgnorePatterns: ['scripts'], +}; + +export default config; diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json new file mode 100644 index 00000000..7adb30f7 --- /dev/null +++ b/packages/mcp-server/manifest.json @@ -0,0 +1,57 @@ +{ + "dxt_version": "0.2", + "name": "imagekit-api-mcp", + "version": "0.0.1-alpha.0", + "description": "The official MCP Server for the Image Kit API", + "author": { + "name": "Image Kit", + "email": "developer@imagekit.io" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/imagekit-developer/imagekit-nodejs.git" + }, + "homepage": "https://github.com/imagekit-developer/imagekit-nodejs/tree/main/packages/mcp-server#readme", + "documentation": "https://imagekit.io/docs/api-reference", + "server": { + "type": "node", + "entry_point": "index.js", + "mcp_config": { + "command": "node", + "args": ["${__dirname}/index.js"], + "env": { + "IMAGEKIT_PRIVATE_KEY": "${user_config.IMAGEKIT_PRIVATE_KEY}", + "OPTIONAL_IMAGEKIT_IGNORES_THIS": "${user_config.OPTIONAL_IMAGEKIT_IGNORES_THIS}", + "IMAGEKIT_WEBHOOK_SECRET": "${user_config.IMAGEKIT_WEBHOOK_SECRET}" + } + } + }, + "user_config": { + "IMAGEKIT_PRIVATE_KEY": { + "title": "private_key", + "description": "Your ImageKit private API key (starts with `private_`).\nYou can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys).\n", + "required": true, + "type": "string" + }, + "OPTIONAL_IMAGEKIT_IGNORES_THIS": { + "title": "password", + "description": "ImageKit uses your API key as username and ignores the password. \nThe SDK sets a dummy value. You can ignore this field.\n", + "required": false, + "type": "string" + }, + "IMAGEKIT_WEBHOOK_SECRET": { + "title": "webhook_secret", + "description": "Your ImageKit webhook secret for verifying webhook signatures (starts with `whsec_`).\nYou can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks).\nOnly required if you're using webhooks.\n", + "required": false, + "type": "string" + } + }, + "tools": [], + "tools_generated": true, + "compatibility": { + "runtimes": { + "node": ">=18.0.0" + } + }, + "keywords": ["api"] +} diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json new file mode 100644 index 00000000..d9d27302 --- /dev/null +++ b/packages/mcp-server/package.json @@ -0,0 +1,86 @@ +{ + "name": "imagekit-api-mcp", + "version": "7.0.0", + "description": "The official MCP Server for the Image Kit API", + "author": "Image Kit ", + "types": "dist/index.d.ts", + "main": "dist/index.js", + "type": "commonjs", + "repository": { + "type": "git", + "url": "git+https://github.com/imagekit-developer/imagekit-nodejs.git", + "directory": "packages/mcp-server" + }, + "homepage": "https://github.com/imagekit-developer/imagekit-nodejs/tree/main/packages/mcp-server#readme", + "license": "Apache-2.0", + "packageManager": "yarn@1.22.22", + "private": false, + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "jest", + "build": "bash ./build", + "prepack": "echo 'to pack, run yarn build && (cd dist; yarn pack)' && exit 1", + "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1", + "format": "prettier --write --cache --cache-strategy metadata . !dist", + "prepare": "npm run build", + "tsn": "ts-node -r tsconfig-paths/register", + "lint": "eslint --ext ts,js .", + "fix": "eslint --fix --ext ts,js ." + }, + "dependencies": { + "@imagekit/nodejs": "file:../../dist/", + "@cloudflare/cabidela": "^0.2.4", + "@modelcontextprotocol/sdk": "^1.11.5", + "@valtown/deno-http-worker": "^0.0.21", + "cors": "^2.8.5", + "express": "^5.1.0", + "jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz", + "qs": "^6.14.0", + "yargs": "^17.7.2", + "zod": "^3.25.20", + "zod-to-json-schema": "^3.24.5", + "zod-validation-error": "^4.0.1" + }, + "bin": { + "mcp-server": "dist/index.js" + }, + "devDependencies": { + "@anthropic-ai/mcpb": "^1.1.0", + "@types/cors": "^2.8.19", + "@types/express": "^5.0.3", + "@types/jest": "^29.4.0", + "@types/qs": "^6.14.0", + "@types/yargs": "^17.0.8", + "@typescript-eslint/eslint-plugin": "8.31.1", + "@typescript-eslint/parser": "8.31.1", + "eslint": "^8.49.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-unused-imports": "^3.0.0", + "jest": "^29.4.0", + "prettier": "^3.0.0", + "ts-jest": "^29.1.0", + "ts-morph": "^19.0.0", + "ts-node": "^10.5.0", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsconfig-paths": "^4.0.0", + "typescript": "5.8.3" + }, + "imports": { + "imagekit-api-mcp": ".", + "imagekit-api-mcp/*": "./src/*" + }, + "exports": { + ".": { + "require": "./dist/index.js", + "default": "./dist/index.mjs" + }, + "./*.mjs": "./dist/*.mjs", + "./*.js": "./dist/*.js", + "./*": { + "require": "./dist/*.js", + "default": "./dist/*.mjs" + } + } +} diff --git a/packages/mcp-server/scripts/copy-bundle-files.cjs b/packages/mcp-server/scripts/copy-bundle-files.cjs new file mode 100644 index 00000000..a61be6b8 --- /dev/null +++ b/packages/mcp-server/scripts/copy-bundle-files.cjs @@ -0,0 +1,36 @@ +const fs = require('fs'); +const path = require('path'); +const pkgJson = require('../dist-bundle/package.json'); + +const distDir = path.resolve(__dirname, '..', 'dist'); +const distBundleDir = path.resolve(__dirname, '..', 'dist-bundle'); +const distBundlePkgJson = path.join(distBundleDir, 'package.json'); + +async function* walk(dir) { + for await (const d of await fs.promises.opendir(dir)) { + const entry = path.join(dir, d.name); + if (d.isDirectory()) yield* walk(entry); + else if (d.isFile()) yield entry; + } +} + +async function copyFiles() { + // copy runtime files + for await (const file of walk(distDir)) { + if (!/[cm]?js$/.test(file)) continue; + const dest = path.join(distBundleDir, path.relative(distDir, file)); + await fs.promises.mkdir(path.dirname(dest), { recursive: true }); + await fs.promises.copyFile(file, dest); + } + + // replace package.json reference with local reference + for (const dep in pkgJson.dependencies) { + if (dep === '@imagekit/nodejs') { + pkgJson.dependencies[dep] = 'file:../../../dist/'; + } + } + + await fs.promises.writeFile(distBundlePkgJson, JSON.stringify(pkgJson, null, 2)); +} + +copyFiles(); diff --git a/packages/mcp-server/scripts/postprocess-dist-package-json.cjs b/packages/mcp-server/scripts/postprocess-dist-package-json.cjs new file mode 100644 index 00000000..2c75a6cd --- /dev/null +++ b/packages/mcp-server/scripts/postprocess-dist-package-json.cjs @@ -0,0 +1,12 @@ +const fs = require('fs'); +const pkgJson = require('../dist/package.json'); +const parentPkgJson = require('../../../package.json'); + +for (const dep in pkgJson.dependencies) { + // ensure we point to NPM instead of a local directory + if (dep === '@imagekit/nodejs') { + pkgJson.dependencies[dep] = '^' + parentPkgJson.version; + } +} + +fs.writeFileSync('dist/package.json', JSON.stringify(pkgJson, null, 2)); diff --git a/packages/mcp-server/src/code-tool-paths.cts b/packages/mcp-server/src/code-tool-paths.cts new file mode 100644 index 00000000..15ce7f55 --- /dev/null +++ b/packages/mcp-server/src/code-tool-paths.cts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export const workerPath = require.resolve('./code-tool-worker.mjs'); diff --git a/packages/mcp-server/src/code-tool-types.ts b/packages/mcp-server/src/code-tool-types.ts new file mode 100644 index 00000000..02e7e890 --- /dev/null +++ b/packages/mcp-server/src/code-tool-types.ts @@ -0,0 +1,14 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { ClientOptions } from '@imagekit/nodejs'; + +export type WorkerInput = { + opts: ClientOptions; + code: string; +}; +export type WorkerSuccess = { + result: unknown | null; + logLines: string[]; + errLines: string[]; +}; +export type WorkerError = { message: string | undefined }; diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts new file mode 100644 index 00000000..865c3928 --- /dev/null +++ b/packages/mcp-server/src/code-tool-worker.ts @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import util from 'node:util'; +import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types'; +import { ImageKit } from '@imagekit/nodejs'; + +const fetch = async (req: Request): Promise => { + const { opts, code } = (await req.json()) as WorkerInput; + const client = new ImageKit({ + ...opts, + }); + + const logLines: string[] = []; + const errLines: string[] = []; + const console = { + log: (...args: unknown[]) => { + logLines.push(util.format(...args)); + }, + error: (...args: unknown[]) => { + errLines.push(util.format(...args)); + }, + }; + try { + let run_ = async (client: any) => {}; + eval(` + ${code} + run_ = run; + `); + const result = await run_(client); + return Response.json({ + result, + logLines, + errLines, + } satisfies WorkerSuccess); + } catch (e) { + const message = e instanceof Error ? e.message : undefined; + return Response.json( + { + message, + } satisfies WorkerError, + { status: 400, statusText: 'Code execution error' }, + ); + } +}; + +export default { fetch }; diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts new file mode 100644 index 00000000..47d2064a --- /dev/null +++ b/packages/mcp-server/src/code-tool.ts @@ -0,0 +1,148 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { dirname } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import ImageKit, { ClientOptions } from '@imagekit/nodejs'; +import { Endpoint, ContentBlock, Metadata } from './tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types'; + +/** + * A tool that runs code against a copy of the SDK. + * + * Instead of exposing every endpoint as it's own tool, which uses up too many tokens for LLMs to use at once, + * we expose a single tool that can be used to search for endpoints by name, resource, operation, or tag, and then + * a generic endpoint that can be used to invoke any endpoint with the provided arguments. + * + * @param endpoints - The endpoints to include in the list. + */ +export async function codeTool(): Promise { + const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] }; + const tool: Tool = { + name: 'execute', + description: + 'Runs Typescript code to interact with the API.\nYou are a skilled programmer writing code to interface with the service.\nDefine an async function named "run" that takes a single parameter of an initialized client, and it will be run.\nDo not initialize a client, but instead use the client that you are given as a parameter.\nYou will be returned anything that your function returns, plus the results of any console.log statements.\nIf any code triggers an error, the tool will return an error response, so you do not need to add error handling unless you want to output something more helpful than the raw error.\nIt is not necessary to add comments to code, unless by adding those comments you believe that you can generate better code.\nThis code will run in a container, and you will not be able to use fetch or otherwise interact with the network calls other than through the client you are given.\nAny variables you define won\'t live between successive uses of this call, so make sure to return or log any data you might need later.', + inputSchema: { type: 'object', properties: { code: { type: 'string' } } }, + }; + + // Import dynamically to avoid failing at import time in cases where the environment is not well-supported. + const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker'); + const { workerPath } = await import('./code-tool-paths.cjs'); + + const handler = async (client: ImageKit, args: unknown) => { + const baseURLHostname = new URL(client.baseURL).hostname; + const { code } = args as { code: string }; + + const worker = await newDenoHTTPWorker(pathToFileURL(workerPath), { + runFlags: [ + `--node-modules-dir=manual`, + `--allow-read=code-tool-worker.mjs,${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`, + `--allow-net=${baseURLHostname}`, + // Allow environment variables because instantiating the client will try to read from them, + // even though they are not set. + '--allow-env', + ], + printOutput: true, + spawnOptions: { + cwd: dirname(workerPath), + }, + }); + + try { + const resp = await new Promise((resolve, reject) => { + worker.addEventListener('exit', (exitCode) => { + reject(new Error(`Worker exited with code ${exitCode}`)); + }); + + const opts: ClientOptions = { + baseURL: client.baseURL, + privateKey: client.privateKey, + password: client.password, + webhookSecret: client.webhookSecret, + defaultHeaders: { + 'X-Stainless-MCP': 'true', + }, + }; + + const req = worker.request( + 'http://localhost', + { + headers: { + 'content-type': 'application/json', + }, + method: 'POST', + }, + (resp) => { + const body: Uint8Array[] = []; + resp.on('error', (err) => { + reject(err); + }); + resp.on('data', (chunk) => { + body.push(chunk); + }); + resp.on('end', () => { + resolve( + new Response(Buffer.concat(body).toString(), { + status: resp.statusCode ?? 200, + headers: resp.headers as any, + }), + ); + }); + }, + ); + + const body = JSON.stringify({ + opts, + code, + } satisfies WorkerInput); + + req.write(body, (err) => { + if (err !== null && err !== undefined) { + reject(err); + } + }); + + req.end(); + }); + + if (resp.status === 200) { + const { result, logLines, errLines } = (await resp.json()) as WorkerSuccess; + const returnOutput: ContentBlock | null = + result === null ? null + : result === undefined ? null + : { + type: 'text', + text: typeof result === 'string' ? (result as string) : JSON.stringify(result), + }; + const logOutput: ContentBlock | null = + logLines.length === 0 ? + null + : { + type: 'text', + text: logLines.join('\n'), + }; + const errOutput: ContentBlock | null = + errLines.length === 0 ? + null + : { + type: 'text', + text: 'Error output:\n' + errLines.join('\n'), + }; + return { + content: [returnOutput, logOutput, errOutput].filter((block) => block !== null), + }; + } else { + const { message } = (await resp.json()) as WorkerError; + throw new Error(message); + } + } catch (e) { + throw e; + } finally { + worker.terminate(); + } + }; + + return { metadata, tool, handler }; +} diff --git a/packages/mcp-server/src/compat.ts b/packages/mcp-server/src/compat.ts new file mode 100644 index 00000000..f84053c7 --- /dev/null +++ b/packages/mcp-server/src/compat.ts @@ -0,0 +1,483 @@ +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { z } from 'zod'; +import { Endpoint } from './tools'; + +export interface ClientCapabilities { + topLevelUnions: boolean; + validJson: boolean; + refs: boolean; + unions: boolean; + formats: boolean; + toolNameLength: number | undefined; +} + +export const defaultClientCapabilities: ClientCapabilities = { + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, +}; + +export const ClientType = z.enum(['openai-agents', 'claude', 'claude-code', 'cursor', 'infer']); +export type ClientType = z.infer; + +// Client presets for compatibility +// Note that these could change over time as models get better, so this is +// a best effort. +export const knownClients: Record, ClientCapabilities> = { + 'openai-agents': { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + claude: { + topLevelUnions: true, + validJson: false, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + 'claude-code': { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + cursor: { + topLevelUnions: false, + validJson: true, + refs: false, + unions: false, + formats: false, + toolNameLength: 50, + }, +}; + +/** + * Attempts to parse strings into JSON objects + */ +export function parseEmbeddedJSON(args: Record, schema: Record) { + let updated = false; + const newArgs: Record = Object.assign({}, args); + + for (const [key, value] of Object.entries(newArgs)) { + if (typeof value === 'string') { + try { + const parsed = JSON.parse(value); + // Only parse if result is a plain object (not array, null, or primitive) + if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { + newArgs[key] = parsed; + updated = true; + } + } catch (e) { + // Not valid JSON, leave as is + } + } + } + + if (updated) { + return newArgs; + } + + return args; +} + +export type JSONSchema = { + type?: string; + properties?: Record; + required?: string[]; + anyOf?: JSONSchema[]; + $ref?: string; + $defs?: Record; + [key: string]: any; +}; + +/** + * Truncates tool names to the specified length while ensuring uniqueness. + * If truncation would cause duplicate names, appends a number to make them unique. + */ +export function truncateToolNames(names: string[], maxLength: number): Map { + if (maxLength <= 0) { + return new Map(); + } + + const renameMap = new Map(); + const usedNames = new Set(); + + const toTruncate = names.filter((name) => name.length > maxLength); + + if (toTruncate.length === 0) { + return renameMap; + } + + const willCollide = + new Set(toTruncate.map((name) => name.slice(0, maxLength - 1))).size < toTruncate.length; + + if (!willCollide) { + for (const name of toTruncate) { + const truncatedName = name.slice(0, maxLength); + renameMap.set(name, truncatedName); + } + } else { + const baseLength = maxLength - 1; + + for (const name of toTruncate) { + const baseName = name.slice(0, baseLength); + let counter = 1; + + while (usedNames.has(baseName + counter)) { + counter++; + } + + const finalName = baseName + counter; + renameMap.set(name, finalName); + usedNames.add(finalName); + } + } + + return renameMap; +} + +/** + * Removes top-level unions from a tool by splitting it into multiple tools, + * one for each variant in the union. + */ +export function removeTopLevelUnions(tool: Tool): Tool[] { + const inputSchema = tool.inputSchema as JSONSchema; + const variants = inputSchema.anyOf; + + if (!variants || !Array.isArray(variants) || variants.length === 0) { + return [tool]; + } + + const defs = inputSchema.$defs || {}; + + return variants.map((variant, index) => { + const variantSchema: JSONSchema = { + ...inputSchema, + ...variant, + type: 'object', + properties: { + ...(inputSchema.properties || {}), + ...(variant.properties || {}), + }, + }; + + delete variantSchema.anyOf; + + if (!variantSchema['description']) { + variantSchema['description'] = tool.description; + } + + const usedDefs = findUsedDefs(variant, defs); + if (Object.keys(usedDefs).length > 0) { + variantSchema.$defs = usedDefs; + } else { + delete variantSchema.$defs; + } + + return { + ...tool, + name: `${tool.name}_${toSnakeCase(variant['title'] || `variant${index + 1}`)}`, + description: variant['description'] || tool.description, + inputSchema: variantSchema, + } as Tool; + }); +} + +function findUsedDefs( + schema: JSONSchema, + defs: Record, + visited: Set = new Set(), +): Record { + const usedDefs: Record = {}; + + if (typeof schema !== 'object' || schema === null) { + return usedDefs; + } + + if (schema.$ref) { + const refParts = schema.$ref.split('/'); + if (refParts[0] === '#' && refParts[1] === '$defs' && refParts[2]) { + const defName = refParts[2]; + const def = defs[defName]; + if (def && !visited.has(schema.$ref)) { + usedDefs[defName] = def; + visited.add(schema.$ref); + Object.assign(usedDefs, findUsedDefs(def, defs, visited)); + visited.delete(schema.$ref); + } + } + return usedDefs; + } + + for (const key in schema) { + if (key !== '$defs' && typeof schema[key] === 'object' && schema[key] !== null) { + Object.assign(usedDefs, findUsedDefs(schema[key] as JSONSchema, defs, visited)); + } + } + + return usedDefs; +} + +// Export for testing +export { findUsedDefs }; + +/** + * Inlines all $refs in a schema, eliminating $defs. + * If a circular reference is detected, the circular property is removed. + */ +export function inlineRefs(schema: JSONSchema): JSONSchema { + if (!schema || typeof schema !== 'object') { + return schema; + } + + const clonedSchema = { ...schema }; + const defs: Record = schema.$defs || {}; + + delete clonedSchema.$defs; + + const result = inlineRefsRecursive(clonedSchema, defs, new Set()); + // The top level can never be null + return result === null ? {} : result; +} + +function inlineRefsRecursive( + schema: JSONSchema, + defs: Record, + refPath: Set, +): JSONSchema | null { + if (!schema || typeof schema !== 'object') { + return schema; + } + + if (Array.isArray(schema)) { + return schema.map((item) => { + const processed = inlineRefsRecursive(item, defs, refPath); + return processed === null ? {} : processed; + }) as JSONSchema; + } + + const result = { ...schema }; + + if ('$ref' in result && typeof result.$ref === 'string') { + if (result.$ref.startsWith('#/$defs/')) { + const refName = result.$ref.split('/').pop() as string; + const def = defs[refName]; + + // If we've already seen this ref in our path, we have a circular reference + if (refPath.has(result.$ref)) { + // For circular references, we completely remove the property + // by returning null. The parent will remove it. + return null; + } + + if (def) { + const newRefPath = new Set(refPath); + newRefPath.add(result.$ref); + + const inlinedDef = inlineRefsRecursive({ ...def }, defs, newRefPath); + + if (inlinedDef === null) { + return { ...result }; + } + + // Merge the inlined definition with the original schema's properties + // but preserve things like description, etc. + const { $ref, ...rest } = result; + return { ...inlinedDef, ...rest }; + } + } + + // Keep external refs as-is + return result; + } + + for (const key in result) { + if (result[key] && typeof result[key] === 'object') { + const processed = inlineRefsRecursive(result[key] as JSONSchema, defs, refPath); + if (processed === null) { + // Remove properties that would cause circular references + delete result[key]; + } else { + result[key] = processed; + } + } + } + + return result; +} + +/** + * Removes anyOf fields from a schema, using only the first variant. + */ +export function removeAnyOf(schema: JSONSchema): JSONSchema { + if (!schema || typeof schema !== 'object') { + return schema; + } + + if (Array.isArray(schema)) { + return schema.map((item) => removeAnyOf(item)) as JSONSchema; + } + + const result = { ...schema }; + + if ('anyOf' in result && Array.isArray(result.anyOf) && result.anyOf.length > 0) { + const firstVariant = result.anyOf[0]; + + if (firstVariant && typeof firstVariant === 'object') { + // Special handling for properties to ensure deep merge + if (firstVariant.properties && result.properties) { + result.properties = { + ...result.properties, + ...(firstVariant.properties as Record), + }; + } else if (firstVariant.properties) { + result.properties = { ...firstVariant.properties }; + } + + for (const key in firstVariant) { + if (key !== 'properties') { + result[key] = firstVariant[key]; + } + } + } + + delete result.anyOf; + } + + for (const key in result) { + if (result[key] && typeof result[key] === 'object') { + result[key] = removeAnyOf(result[key] as JSONSchema); + } + } + + return result; +} + +/** + * Removes format fields from a schema and appends them to the description. + */ +export function removeFormats(schema: JSONSchema, formatsCapability: boolean): JSONSchema { + if (formatsCapability) { + return schema; + } + + if (!schema || typeof schema !== 'object') { + return schema; + } + + if (Array.isArray(schema)) { + return schema.map((item) => removeFormats(item, formatsCapability)) as JSONSchema; + } + + const result = { ...schema }; + + if ('format' in result && typeof result['format'] === 'string') { + const formatStr = `(format: "${result['format']}")`; + + if ('description' in result && typeof result['description'] === 'string') { + result['description'] = `${result['description']} ${formatStr}`; + } else { + result['description'] = formatStr; + } + + delete result['format']; + } + + for (const key in result) { + if (result[key] && typeof result[key] === 'object') { + result[key] = removeFormats(result[key] as JSONSchema, formatsCapability); + } + } + + return result; +} + +/** + * Applies all compatibility transformations to the endpoints based on the provided capabilities. + */ +export function applyCompatibilityTransformations( + endpoints: Endpoint[], + capabilities: ClientCapabilities, +): Endpoint[] { + let transformedEndpoints = [...endpoints]; + + // Handle top-level unions first as this changes tool names + if (!capabilities.topLevelUnions) { + const newEndpoints: Endpoint[] = []; + + for (const endpoint of transformedEndpoints) { + const variantTools = removeTopLevelUnions(endpoint.tool); + + if (variantTools.length === 1) { + newEndpoints.push(endpoint); + } else { + for (const variantTool of variantTools) { + newEndpoints.push({ + ...endpoint, + tool: variantTool, + }); + } + } + } + + transformedEndpoints = newEndpoints; + } + + if (capabilities.toolNameLength) { + const toolNames = transformedEndpoints.map((endpoint) => endpoint.tool.name); + const renameMap = truncateToolNames(toolNames, capabilities.toolNameLength); + + transformedEndpoints = transformedEndpoints.map((endpoint) => ({ + ...endpoint, + tool: { + ...endpoint.tool, + name: renameMap.get(endpoint.tool.name) ?? endpoint.tool.name, + }, + })); + } + + if (!capabilities.refs || !capabilities.unions || !capabilities.formats) { + transformedEndpoints = transformedEndpoints.map((endpoint) => { + let schema = endpoint.tool.inputSchema as JSONSchema; + + if (!capabilities.refs) { + schema = inlineRefs(schema); + } + + if (!capabilities.unions) { + schema = removeAnyOf(schema); + } + + if (!capabilities.formats) { + schema = removeFormats(schema, capabilities.formats); + } + + return { + ...endpoint, + tool: { + ...endpoint.tool, + inputSchema: schema as typeof endpoint.tool.inputSchema, + }, + }; + }); + } + + return transformedEndpoints; +} + +function toSnakeCase(str: string): string { + return str + .replace(/\s+/g, '_') + .replace(/([a-z])([A-Z])/g, '$1_$2') + .toLowerCase(); +} diff --git a/packages/mcp-server/src/dynamic-tools.ts b/packages/mcp-server/src/dynamic-tools.ts new file mode 100644 index 00000000..47d60e0d --- /dev/null +++ b/packages/mcp-server/src/dynamic-tools.ts @@ -0,0 +1,153 @@ +import ImageKit from '@imagekit/nodejs'; +import { Endpoint, asTextContentResult, ToolCallResult } from './tools/types'; +import { zodToJsonSchema } from 'zod-to-json-schema'; +import { z } from 'zod'; +import { Cabidela } from '@cloudflare/cabidela'; + +function zodToInputSchema(schema: z.ZodSchema) { + return { + type: 'object' as const, + ...(zodToJsonSchema(schema) as any), + }; +} + +/** + * A list of tools that expose all the endpoints in the API dynamically. + * + * Instead of exposing every endpoint as it's own tool, which uses up too many tokens for LLMs to use at once, + * we expose a single tool that can be used to search for endpoints by name, resource, operation, or tag, and then + * a generic endpoint that can be used to invoke any endpoint with the provided arguments. + * + * @param endpoints - The endpoints to include in the list. + */ +export function dynamicTools(endpoints: Endpoint[]): Endpoint[] { + const listEndpointsSchema = z.object({ + search_query: z + .string() + .optional() + .describe( + 'An optional search query to filter the endpoints by. Provide a partial name, resource, operation, or tag to filter the endpoints returned.', + ), + }); + + const listEndpointsTool = { + metadata: { + resource: 'dynamic_tools', + operation: 'read' as const, + tags: [], + }, + tool: { + name: 'list_api_endpoints', + description: 'List or search for all endpoints in the Image Kit TypeScript API', + inputSchema: zodToInputSchema(listEndpointsSchema), + }, + handler: async (client: ImageKit, args: Record | undefined): Promise => { + const query = args && listEndpointsSchema.parse(args).search_query?.trim(); + + const filteredEndpoints = + query && query.length > 0 ? + endpoints.filter((endpoint) => { + const fieldsToMatch = [ + endpoint.tool.name, + endpoint.tool.description, + endpoint.metadata.resource, + endpoint.metadata.operation, + ...endpoint.metadata.tags, + ]; + return fieldsToMatch.some((field) => field && field.toLowerCase().includes(query.toLowerCase())); + }) + : endpoints; + + return asTextContentResult({ + tools: filteredEndpoints.map(({ tool, metadata }) => ({ + name: tool.name, + description: tool.description, + resource: metadata.resource, + operation: metadata.operation, + tags: metadata.tags, + })), + }); + }, + }; + + const getEndpointSchema = z.object({ + endpoint: z.string().describe('The name of the endpoint to get the schema for.'), + }); + const getEndpointTool = { + metadata: { + resource: 'dynamic_tools', + operation: 'read' as const, + tags: [], + }, + tool: { + name: 'get_api_endpoint_schema', + description: + 'Get the schema for an endpoint in the Image Kit TypeScript API. You can use the schema returned by this tool to invoke an endpoint with the `invoke_api_endpoint` tool.', + inputSchema: zodToInputSchema(getEndpointSchema), + }, + handler: async (client: ImageKit, args: Record | undefined) => { + if (!args) { + throw new Error('No endpoint provided'); + } + const endpointName = getEndpointSchema.parse(args).endpoint; + + const endpoint = endpoints.find((e) => e.tool.name === endpointName); + if (!endpoint) { + throw new Error(`Endpoint ${endpointName} not found`); + } + return asTextContentResult(endpoint.tool); + }, + }; + + const invokeEndpointSchema = z.object({ + endpoint_name: z.string().describe('The name of the endpoint to invoke.'), + args: z + .record(z.string(), z.any()) + .describe( + 'The arguments to pass to the endpoint. This must match the schema returned by the `get_api_endpoint_schema` tool.', + ), + }); + + const invokeEndpointTool = { + metadata: { + resource: 'dynamic_tools', + operation: 'write' as const, + tags: [], + }, + tool: { + name: 'invoke_api_endpoint', + description: + 'Invoke an endpoint in the Image Kit TypeScript API. Note: use the `list_api_endpoints` tool to get the list of endpoints and `get_api_endpoint_schema` tool to get the schema for an endpoint.', + inputSchema: zodToInputSchema(invokeEndpointSchema), + }, + handler: async (client: ImageKit, args: Record | undefined): Promise => { + if (!args) { + throw new Error('No endpoint provided'); + } + const { success, data, error } = invokeEndpointSchema.safeParse(args); + if (!success) { + throw new Error(`Invalid arguments for endpoint. ${error?.format()}`); + } + const { endpoint_name, args: endpointArgs } = data; + + const endpoint = endpoints.find((e) => e.tool.name === endpoint_name); + if (!endpoint) { + throw new Error( + `Endpoint ${endpoint_name} not found. Use the \`list_api_endpoints\` tool to get the list of available endpoints.`, + ); + } + + try { + // Try to validate the arguments for a better error message + const cabidela = new Cabidela(endpoint.tool.inputSchema, { fullErrors: true }); + cabidela.validate(endpointArgs); + } catch (error) { + throw new Error(`Invalid arguments for endpoint ${endpoint_name}:\n${error}`); + } + + return await endpoint.handler(client, endpointArgs); + }, + }; + + return [getEndpointTool, listEndpointsTool, invokeEndpointTool]; +} diff --git a/packages/mcp-server/src/filtering.ts b/packages/mcp-server/src/filtering.ts new file mode 100644 index 00000000..1aa9a40c --- /dev/null +++ b/packages/mcp-server/src/filtering.ts @@ -0,0 +1,14 @@ +// @ts-nocheck +import initJq from 'jq-web'; + +export async function maybeFilter(jqFilter: unknown | undefined, response: any): Promise { + if (jqFilter && typeof jqFilter === 'string') { + return await jq(response, jqFilter); + } else { + return response; + } +} + +async function jq(json: any, jqFilter: string) { + return (await initJq).json(json, jqFilter); +} diff --git a/packages/mcp-server/src/headers.ts b/packages/mcp-server/src/headers.ts new file mode 100644 index 00000000..63e3abc9 --- /dev/null +++ b/packages/mcp-server/src/headers.ts @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { IncomingMessage } from 'node:http'; +import { ClientOptions } from '@imagekit/nodejs'; + +export const parseAuthHeaders = (req: IncomingMessage): Partial => { + if (req.headers.authorization) { + const scheme = req.headers.authorization.split(' ')[0]!; + const value = req.headers.authorization.slice(scheme.length + 1); + switch (scheme) { + case 'Basic': + const rawValue = Buffer.from(value, 'base64').toString(); + return { + privateKey: rawValue.slice(0, rawValue.search(':')), + password: rawValue.slice(rawValue.search(':') + 1), + }; + default: + throw new Error(`Unsupported authorization scheme`); + } + } + + const privateKey = + Array.isArray(req.headers['x-imagekit-private-key']) ? + req.headers['x-imagekit-private-key'][0] + : req.headers['x-imagekit-private-key']; + const password = + Array.isArray(req.headers['x-optional-imagekit-ignores-this']) ? + req.headers['x-optional-imagekit-ignores-this'][0] + : req.headers['x-optional-imagekit-ignores-this']; + return { privateKey, password }; +}; diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts new file mode 100644 index 00000000..ec34ab47 --- /dev/null +++ b/packages/mcp-server/src/http.ts @@ -0,0 +1,127 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; + +import express from 'express'; +import { fromError } from 'zod-validation-error/v3'; +import { McpOptions, parseQueryOptions } from './options'; +import { ClientOptions, initMcpServer, newMcpServer } from './server'; +import { parseAuthHeaders } from './headers'; + +const newServer = ({ + clientOptions, + mcpOptions: defaultMcpOptions, + req, + res, +}: { + clientOptions: ClientOptions; + mcpOptions: McpOptions; + req: express.Request; + res: express.Response; +}): McpServer | null => { + const server = newMcpServer(); + + let mcpOptions: McpOptions; + try { + mcpOptions = parseQueryOptions(defaultMcpOptions, req.query); + } catch (error) { + res.status(400).json({ + jsonrpc: '2.0', + error: { + code: -32000, + message: `Invalid request: ${fromError(error)}`, + }, + }); + return null; + } + + try { + const authOptions = parseAuthHeaders(req); + initMcpServer({ + server: server, + clientOptions: { + ...clientOptions, + ...authOptions, + }, + mcpOptions, + }); + } catch { + res.status(401).json({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Unauthorized', + }, + }); + return null; + } + + return server; +}; + +const post = + (options: { clientOptions: ClientOptions; mcpOptions: McpOptions }) => + async (req: express.Request, res: express.Response) => { + const server = newServer({ ...options, req, res }); + // If we return null, we already set the authorization error. + if (server === null) return; + const transport = new StreamableHTTPServerTransport({ + // Stateless server + sessionIdGenerator: undefined, + }); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + }; + +const get = async (req: express.Request, res: express.Response) => { + res.status(405).json({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not supported', + }, + }); +}; + +const del = async (req: express.Request, res: express.Response) => { + res.status(405).json({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not supported', + }, + }); +}; + +export const streamableHTTPApp = ({ + clientOptions = {}, + mcpOptions = {}, +}: { + clientOptions?: ClientOptions; + mcpOptions?: McpOptions; +}): express.Express => { + const app = express(); + app.set('query parser', 'extended'); + app.use(express.json()); + + app.get('/', get); + app.post('/', post({ clientOptions, mcpOptions })); + app.delete('/', del); + + return app; +}; + +export const launchStreamableHTTPServer = async (options: McpOptions, port: number | string | undefined) => { + const app = streamableHTTPApp({ mcpOptions: options }); + const server = app.listen(port); + const address = server.address(); + + if (typeof address === 'string') { + console.error(`MCP Server running on streamable HTTP at ${address}`); + } else if (address !== null) { + console.error(`MCP Server running on streamable HTTP on port ${address.port}`); + } else { + console.error(`MCP Server running on streamable HTTP on port ${port}`); + } +}; diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts new file mode 100644 index 00000000..4850a0e2 --- /dev/null +++ b/packages/mcp-server/src/index.ts @@ -0,0 +1,108 @@ +#!/usr/bin/env node + +import { selectTools } from './server'; +import { Endpoint, endpoints } from './tools'; +import { McpOptions, parseCLIOptions } from './options'; +import { launchStdioServer } from './stdio'; +import { launchStreamableHTTPServer } from './http'; + +async function main() { + const options = parseOptionsOrError(); + + if (options.list) { + listAllTools(); + return; + } + + const selectedTools = await selectToolsOrError(endpoints, options); + + console.error( + `MCP Server starting with ${selectedTools.length} tools:`, + selectedTools.map((e) => e.tool.name), + ); + + switch (options.transport) { + case 'stdio': + await launchStdioServer(options); + break; + case 'http': + await launchStreamableHTTPServer(options, options.port ?? options.socket); + break; + } +} + +if (require.main === module) { + main().catch((error) => { + console.error('Fatal error in main():', error); + process.exit(1); + }); +} + +function parseOptionsOrError() { + try { + return parseCLIOptions(); + } catch (error) { + console.error('Error parsing options:', error); + process.exit(1); + } +} + +async function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Promise { + try { + const includedTools = await selectTools(endpoints, options); + if (includedTools.length === 0) { + console.error('No tools match the provided filters.'); + process.exit(1); + } + return includedTools; + } catch (error) { + if (error instanceof Error) { + console.error('Error filtering tools:', error.message); + } else { + console.error('Error filtering tools:', error); + } + process.exit(1); + } +} + +function listAllTools() { + if (endpoints.length === 0) { + console.log('No tools available.'); + return; + } + console.log('Available tools:\n'); + + // Group endpoints by resource + const resourceGroups = new Map(); + + for (const endpoint of endpoints) { + const resource = endpoint.metadata.resource; + if (!resourceGroups.has(resource)) { + resourceGroups.set(resource, []); + } + resourceGroups.get(resource)!.push(endpoint); + } + + // Sort resources alphabetically + const sortedResources = Array.from(resourceGroups.keys()).sort(); + + // Display hierarchically by resource + for (const resource of sortedResources) { + console.log(`Resource: ${resource}`); + + const resourceEndpoints = resourceGroups.get(resource)!; + // Sort endpoints by tool name + resourceEndpoints.sort((a, b) => a.tool.name.localeCompare(b.tool.name)); + + for (const endpoint of resourceEndpoints) { + const { + tool, + metadata: { operation, tags }, + } = endpoint; + + console.log(` - ${tool.name} (${operation}) ${tags.length > 0 ? `tags: ${tags.join(', ')}` : ''}`); + console.log(` Description: ${tool.description}`); + } + console.log(''); + } +} diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts new file mode 100644 index 00000000..ecc9f10e --- /dev/null +++ b/packages/mcp-server/src/options.ts @@ -0,0 +1,456 @@ +import qs from 'qs'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import z from 'zod'; +import { endpoints, Filter } from './tools'; +import { ClientCapabilities, knownClients, ClientType } from './compat'; + +export type CLIOptions = McpOptions & { + list: boolean; + transport: 'stdio' | 'http'; + port: number | undefined; + socket: string | undefined; +}; + +export type McpOptions = { + client?: ClientType | undefined; + includeDynamicTools?: boolean | undefined; + includeAllTools?: boolean | undefined; + includeCodeTools?: boolean | undefined; + filters?: Filter[] | undefined; + capabilities?: Partial | undefined; +}; + +const CAPABILITY_CHOICES = [ + 'top-level-unions', + 'valid-json', + 'refs', + 'unions', + 'formats', + 'tool-name-length', +] as const; + +type Capability = (typeof CAPABILITY_CHOICES)[number]; + +function parseCapabilityValue(cap: string): { name: Capability; value?: number } { + if (cap.startsWith('tool-name-length=')) { + const parts = cap.split('='); + if (parts.length === 2) { + const length = parseInt(parts[1]!, 10); + if (!isNaN(length)) { + return { name: 'tool-name-length', value: length }; + } + throw new Error(`Invalid tool-name-length value: ${parts[1]}. Expected a number.`); + } + throw new Error(`Invalid format for tool-name-length. Expected tool-name-length=N.`); + } + if (!CAPABILITY_CHOICES.includes(cap as Capability)) { + throw new Error(`Unknown capability: ${cap}. Valid capabilities are: ${CAPABILITY_CHOICES.join(', ')}`); + } + return { name: cap as Capability }; +} + +export function parseCLIOptions(): CLIOptions { + const opts = yargs(hideBin(process.argv)) + .option('tools', { + type: 'string', + array: true, + choices: ['dynamic', 'all', 'code'], + description: 'Use dynamic tools or all tools', + }) + .option('no-tools', { + type: 'string', + array: true, + choices: ['dynamic', 'all', 'code'], + description: 'Do not use any dynamic or all tools', + }) + .option('tool', { + type: 'string', + array: true, + description: 'Include tools matching the specified names', + }) + .option('resource', { + type: 'string', + array: true, + description: 'Include tools matching the specified resources', + }) + .option('operation', { + type: 'string', + array: true, + choices: ['read', 'write'], + description: 'Include tools matching the specified operations', + }) + .option('tag', { + type: 'string', + array: true, + description: 'Include tools with the specified tags', + }) + .option('no-tool', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified names', + }) + .option('no-resource', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified resources', + }) + .option('no-operation', { + type: 'string', + array: true, + description: 'Exclude tools matching the specified operations', + }) + .option('no-tag', { + type: 'string', + array: true, + description: 'Exclude tools with the specified tags', + }) + .option('list', { + type: 'boolean', + description: 'List all tools and exit', + }) + .option('client', { + type: 'string', + choices: Object.keys(knownClients), + description: 'Specify the MCP client being used', + }) + .option('capability', { + type: 'string', + array: true, + description: 'Specify client capabilities', + coerce: (values: string[]) => { + return values.flatMap((v) => v.split(',')); + }, + }) + .option('no-capability', { + type: 'string', + array: true, + description: 'Unset client capabilities', + choices: CAPABILITY_CHOICES, + coerce: (values: string[]) => { + return values.flatMap((v) => v.split(',')); + }, + }) + .option('describe-capabilities', { + type: 'boolean', + description: 'Print detailed explanation of client capabilities and exit', + }) + .option('transport', { + type: 'string', + choices: ['stdio', 'http'], + default: 'stdio', + description: 'What transport to use; stdio for local servers or http for remote servers', + }) + .option('port', { + type: 'number', + description: 'Port to serve on if using http transport', + }) + .option('socket', { + type: 'string', + description: 'Unix socket to serve on if using http transport', + }) + .help(); + + for (const [command, desc] of examples()) { + opts.example(command, desc); + } + + const argv = opts.parseSync(); + + // Handle describe-capabilities flag + if (argv.describeCapabilities) { + console.log(getCapabilitiesExplanation()); + process.exit(0); + } + + const filters: Filter[] = []; + + // Helper function to support comma-separated values + const splitValues = (values: string[] | undefined): string[] => { + if (!values) return []; + return values.flatMap((v) => v.split(',')); + }; + + for (const tag of splitValues(argv.tag)) { + filters.push({ type: 'tag', op: 'include', value: tag }); + } + + for (const tag of splitValues(argv.noTag)) { + filters.push({ type: 'tag', op: 'exclude', value: tag }); + } + + for (const resource of splitValues(argv.resource)) { + filters.push({ type: 'resource', op: 'include', value: resource }); + } + + for (const resource of splitValues(argv.noResource)) { + filters.push({ type: 'resource', op: 'exclude', value: resource }); + } + + for (const tool of splitValues(argv.tool)) { + filters.push({ type: 'tool', op: 'include', value: tool }); + } + + for (const tool of splitValues(argv.noTool)) { + filters.push({ type: 'tool', op: 'exclude', value: tool }); + } + + for (const operation of splitValues(argv.operation)) { + filters.push({ type: 'operation', op: 'include', value: operation }); + } + + for (const operation of splitValues(argv.noOperation)) { + filters.push({ type: 'operation', op: 'exclude', value: operation }); + } + + // Parse client capabilities + const clientCapabilities: Partial = {}; + + // Apply individual capability overrides + if (Array.isArray(argv.capability)) { + for (const cap of argv.capability) { + const parsedCap = parseCapabilityValue(cap); + if (parsedCap.name === 'top-level-unions') { + clientCapabilities.topLevelUnions = true; + } else if (parsedCap.name === 'valid-json') { + clientCapabilities.validJson = true; + } else if (parsedCap.name === 'refs') { + clientCapabilities.refs = true; + } else if (parsedCap.name === 'unions') { + clientCapabilities.unions = true; + } else if (parsedCap.name === 'formats') { + clientCapabilities.formats = true; + } else if (parsedCap.name === 'tool-name-length') { + clientCapabilities.toolNameLength = parsedCap.value; + } + } + } + + // Handle no-capability options to unset capabilities + if (Array.isArray(argv.noCapability)) { + for (const cap of argv.noCapability) { + if (cap === 'top-level-unions') { + clientCapabilities.topLevelUnions = false; + } else if (cap === 'valid-json') { + clientCapabilities.validJson = false; + } else if (cap === 'refs') { + clientCapabilities.refs = false; + } else if (cap === 'unions') { + clientCapabilities.unions = false; + } else if (cap === 'formats') { + clientCapabilities.formats = false; + } else if (cap === 'tool-name-length') { + clientCapabilities.toolNameLength = undefined; + } + } + } + + const shouldIncludeToolType = (toolType: 'dynamic' | 'all' | 'code') => + explicitTools ? argv.tools?.includes(toolType) && !argv.noTools?.includes(toolType) : undefined; + + const explicitTools = Boolean(argv.tools || argv.noTools); + const includeDynamicTools = shouldIncludeToolType('dynamic'); + const includeAllTools = shouldIncludeToolType('all'); + const includeCodeTools = shouldIncludeToolType('code'); + + const transport = argv.transport as 'stdio' | 'http'; + + const client = argv.client as ClientType; + return { + client: client && client !== 'infer' && knownClients[client] ? client : undefined, + includeDynamicTools, + includeAllTools, + includeCodeTools, + filters, + capabilities: clientCapabilities, + list: argv.list || false, + transport, + port: argv.port, + socket: argv.socket, + }; +} + +const coerceArray = (zodType: T) => + z.preprocess( + (val) => + Array.isArray(val) ? val + : val ? [val] + : val, + z.array(zodType).optional(), + ); + +const QueryOptions = z.object({ + tools: coerceArray(z.enum(['dynamic', 'all'])).describe('Use dynamic tools or all tools'), + no_tools: coerceArray(z.enum(['dynamic', 'all'])).describe('Do not use dynamic tools or all tools'), + tool: coerceArray(z.string()).describe('Include tools matching the specified names'), + resource: coerceArray(z.string()).describe('Include tools matching the specified resources'), + operation: coerceArray(z.enum(['read', 'write'])).describe( + 'Include tools matching the specified operations', + ), + tag: coerceArray(z.string()).describe('Include tools with the specified tags'), + no_tool: coerceArray(z.string()).describe('Exclude tools matching the specified names'), + no_resource: coerceArray(z.string()).describe('Exclude tools matching the specified resources'), + no_operation: coerceArray(z.enum(['read', 'write'])).describe( + 'Exclude tools matching the specified operations', + ), + no_tag: coerceArray(z.string()).describe('Exclude tools with the specified tags'), + client: ClientType.optional().describe('Specify the MCP client being used'), + capability: coerceArray(z.string()).describe('Specify client capabilities'), + no_capability: coerceArray(z.enum(CAPABILITY_CHOICES)).describe('Unset client capabilities'), +}); + +export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): McpOptions { + const queryObject = typeof query === 'string' ? qs.parse(query) : query; + const queryOptions = QueryOptions.parse(queryObject); + + const filters: Filter[] = [...(defaultOptions.filters ?? [])]; + + for (const resource of queryOptions.resource || []) { + filters.push({ type: 'resource', op: 'include', value: resource }); + } + for (const operation of queryOptions.operation || []) { + filters.push({ type: 'operation', op: 'include', value: operation }); + } + for (const tag of queryOptions.tag || []) { + filters.push({ type: 'tag', op: 'include', value: tag }); + } + for (const tool of queryOptions.tool || []) { + filters.push({ type: 'tool', op: 'include', value: tool }); + } + for (const resource of queryOptions.no_resource || []) { + filters.push({ type: 'resource', op: 'exclude', value: resource }); + } + for (const operation of queryOptions.no_operation || []) { + filters.push({ type: 'operation', op: 'exclude', value: operation }); + } + for (const tag of queryOptions.no_tag || []) { + filters.push({ type: 'tag', op: 'exclude', value: tag }); + } + for (const tool of queryOptions.no_tool || []) { + filters.push({ type: 'tool', op: 'exclude', value: tool }); + } + + // Parse client capabilities + const clientCapabilities: Partial = { ...defaultOptions.capabilities }; + + for (const cap of queryOptions.capability || []) { + const parsed = parseCapabilityValue(cap); + if (parsed.name === 'top-level-unions') { + clientCapabilities.topLevelUnions = true; + } else if (parsed.name === 'valid-json') { + clientCapabilities.validJson = true; + } else if (parsed.name === 'refs') { + clientCapabilities.refs = true; + } else if (parsed.name === 'unions') { + clientCapabilities.unions = true; + } else if (parsed.name === 'formats') { + clientCapabilities.formats = true; + } else if (parsed.name === 'tool-name-length') { + clientCapabilities.toolNameLength = parsed.value; + } + } + + for (const cap of queryOptions.no_capability || []) { + if (cap === 'top-level-unions') { + clientCapabilities.topLevelUnions = false; + } else if (cap === 'valid-json') { + clientCapabilities.validJson = false; + } else if (cap === 'refs') { + clientCapabilities.refs = false; + } else if (cap === 'unions') { + clientCapabilities.unions = false; + } else if (cap === 'formats') { + clientCapabilities.formats = false; + } else if (cap === 'tool-name-length') { + clientCapabilities.toolNameLength = undefined; + } + } + + let dynamicTools: boolean | undefined = + queryOptions.no_tools && queryOptions.no_tools?.includes('dynamic') ? false + : queryOptions.tools?.includes('dynamic') ? true + : defaultOptions.includeDynamicTools; + + let allTools: boolean | undefined = + queryOptions.no_tools && queryOptions.no_tools?.includes('all') ? false + : queryOptions.tools?.includes('all') ? true + : defaultOptions.includeAllTools; + + return { + client: queryOptions.client ?? defaultOptions.client, + includeDynamicTools: dynamicTools, + includeAllTools: allTools, + includeCodeTools: undefined, + filters, + capabilities: clientCapabilities, + }; +} + +function getCapabilitiesExplanation(): string { + return ` +Client Capabilities Explanation: + +Different Language Models (LLMs) and the MCP clients that use them have varying limitations in how they handle tool schemas. Capability flags allow you to inform the MCP server about these limitations. + +When a capability flag is set to false, the MCP server will automatically adjust the tool schemas to work around that limitation, ensuring broader compatibility. + +Available Capabilities: + +# top-level-unions +Some clients/LLMs do not support JSON schemas with a union type (anyOf) at the root level. If a client lacks this capability, the MCP server splits tools with top-level unions into multiple separate tools, one for each variant in the union. + +# refs +Some clients/LLMs do not support $ref pointers for schema reuse. If a client lacks this capability, the MCP server automatically inlines all references ($defs) directly into the schema. Properties that would cause circular references are removed during this process. + +# valid-json +Some clients/LLMs may incorrectly send arguments as a JSON-encoded string instead of a proper JSON object. If a client *has* this capability, the MCP server will attempt to parse string values as JSON if the initial validation against the schema fails. + +# unions +Some clients/LLMs do not support union types (anyOf) in JSON schemas. If a client lacks this capability, the MCP server removes all anyOf fields and uses only the first variant as the schema. + +# formats +Some clients/LLMs do not support the 'format' keyword in JSON Schema specifications. If a client lacks this capability, the MCP server removes all format fields and appends the format information to the field's description in parentheses. + +# tool-name-length=N +Some clients/LLMs impose a maximum length on tool names. If this capability is set, the MCP server will automatically truncate tool names exceeding the specified length (N), ensuring uniqueness by appending numbers if necessary. + +Client Presets (--client): +Presets like '--client=openai-agents' or '--client=cursor' automatically configure these capabilities based on current known limitations of those clients, simplifying setup. + +Current presets: +${JSON.stringify(knownClients, null, 2)} + `; +} + +function examples(): [string, string][] { + const firstEndpoint = endpoints[0]!; + const secondEndpoint = + endpoints.find((e) => e.metadata.resource !== firstEndpoint.metadata.resource) || endpoints[1]; + const tag = endpoints.find((e) => e.metadata.tags.length > 0)?.metadata.tags[0]; + const otherEndpoint = secondEndpoint || firstEndpoint; + + return [ + [ + `--tool="${firstEndpoint.tool.name}" ${secondEndpoint ? `--tool="${secondEndpoint.tool.name}"` : ''}`, + 'Include tools by name', + ], + [ + `--resource="${firstEndpoint.metadata.resource}" --operation="read"`, + 'Filter by resource and operation', + ], + [ + `--resource="${otherEndpoint.metadata.resource}*" --no-tool="${otherEndpoint.tool.name}"`, + 'Use resource wildcards and exclusions', + ], + [`--client="cursor"`, 'Adjust schemas to be more compatible with Cursor'], + [ + `--capability="top-level-unions" --capability="tool-name-length=40"`, + 'Specify individual client capabilities', + ], + [ + `--client="cursor" --no-capability="tool-name-length"`, + 'Use cursor client preset but remove tool name length limit', + ], + ...(tag ? [[`--tag="${tag}"`, 'Filter based on tags'] as [string, string]] : []), + ]; +} diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts new file mode 100644 index 00000000..789f894f --- /dev/null +++ b/packages/mcp-server/src/server.ts @@ -0,0 +1,204 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { Endpoint, endpoints, HandlerFunction, query } from './tools'; +import { + CallToolRequestSchema, + ListToolsRequestSchema, + SetLevelRequestSchema, + Implementation, + Tool, +} from '@modelcontextprotocol/sdk/types.js'; +import { ClientOptions } from '@imagekit/nodejs'; +import ImageKit from '@imagekit/nodejs'; +import { + applyCompatibilityTransformations, + ClientCapabilities, + defaultClientCapabilities, + knownClients, + parseEmbeddedJSON, +} from './compat'; +import { dynamicTools } from './dynamic-tools'; +import { codeTool } from './code-tool'; +import { McpOptions } from './options'; + +export { McpOptions } from './options'; +export { ClientType } from './compat'; +export { Filter } from './tools'; +export { ClientOptions } from '@imagekit/nodejs'; +export { endpoints } from './tools'; + +export const newMcpServer = () => + new McpServer( + { + name: 'imagekit_nodejs_api', + version: '7.0.0', + }, + { capabilities: { tools: {}, logging: {} } }, + ); + +// Create server instance +export const server = newMcpServer(); + +/** + * Initializes the provided MCP Server with the given tools and handlers. + * If not provided, the default client, tools and handlers will be used. + */ +export function initMcpServer(params: { + server: Server | McpServer; + clientOptions?: ClientOptions; + mcpOptions?: McpOptions; +}) { + const server = params.server instanceof McpServer ? params.server.server : params.server; + const mcpOptions = params.mcpOptions ?? {}; + + let providedEndpoints: Endpoint[] | null = null; + let endpointMap: Record | null = null; + + const initTools = async (implementation?: Implementation) => { + if (implementation && (!mcpOptions.client || mcpOptions.client === 'infer')) { + mcpOptions.client = + implementation.name.toLowerCase().includes('claude') ? 'claude' + : implementation.name.toLowerCase().includes('cursor') ? 'cursor' + : undefined; + mcpOptions.capabilities = { + ...(mcpOptions.client && knownClients[mcpOptions.client]), + ...mcpOptions.capabilities, + }; + } + providedEndpoints ??= await selectTools(endpoints, mcpOptions); + endpointMap ??= Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint])); + }; + + const logAtLevel = + (level: 'debug' | 'info' | 'warning' | 'error') => + (message: string, ...rest: unknown[]) => { + void server.sendLoggingMessage({ + level, + data: { message, rest }, + }); + }; + const logger = { + debug: logAtLevel('debug'), + info: logAtLevel('info'), + warn: logAtLevel('warning'), + error: logAtLevel('error'), + }; + + let client = new ImageKit({ + logger, + ...params.clientOptions, + defaultHeaders: { + ...params.clientOptions?.defaultHeaders, + 'X-Stainless-MCP': 'true', + }, + }); + + server.setRequestHandler(ListToolsRequestSchema, async () => { + if (providedEndpoints === null) { + await initTools(server.getClientVersion()); + } + return { + tools: providedEndpoints!.map((endpoint) => endpoint.tool), + }; + }); + + server.setRequestHandler(CallToolRequestSchema, async (request) => { + if (endpointMap === null) { + await initTools(server.getClientVersion()); + } + const { name, arguments: args } = request.params; + const endpoint = endpointMap![name]; + if (!endpoint) { + throw new Error(`Unknown tool: ${name}`); + } + + return executeHandler(endpoint.tool, endpoint.handler, client, args, mcpOptions.capabilities); + }); + + server.setRequestHandler(SetLevelRequestSchema, async (request) => { + const { level } = request.params; + switch (level) { + case 'debug': + client = client.withOptions({ logLevel: 'debug' }); + break; + case 'info': + client = client.withOptions({ logLevel: 'info' }); + break; + case 'notice': + case 'warning': + client = client.withOptions({ logLevel: 'warn' }); + break; + case 'error': + client = client.withOptions({ logLevel: 'error' }); + break; + default: + client = client.withOptions({ logLevel: 'off' }); + break; + } + return {}; + }); +} + +/** + * Selects the tools to include in the MCP Server based on the provided options. + */ +export async function selectTools(endpoints: Endpoint[], options?: McpOptions): Promise { + const filteredEndpoints = query(options?.filters ?? [], endpoints); + + let includedTools = filteredEndpoints; + + if (includedTools.length > 0) { + if (options?.includeDynamicTools) { + includedTools = dynamicTools(includedTools); + } + } else { + if (options?.includeAllTools) { + includedTools = endpoints; + } else if (options?.includeDynamicTools) { + includedTools = dynamicTools(endpoints); + } else if (options?.includeCodeTools) { + includedTools = [await codeTool()]; + } else { + includedTools = endpoints; + } + } + + const capabilities = { ...defaultClientCapabilities, ...options?.capabilities }; + return applyCompatibilityTransformations(includedTools, capabilities); +} + +/** + * Runs the provided handler with the given client and arguments. + */ +export async function executeHandler( + tool: Tool, + handler: HandlerFunction, + client: ImageKit, + args: Record | undefined, + compatibilityOptions?: Partial, +) { + const options = { ...defaultClientCapabilities, ...compatibilityOptions }; + if (!options.validJson && args) { + args = parseEmbeddedJSON(args, tool.inputSchema); + } + return await handler(client, args || {}); +} + +export const readEnv = (env: string): string | undefined => { + if (typeof (globalThis as any).process !== 'undefined') { + return (globalThis as any).process.env?.[env]?.trim(); + } else if (typeof (globalThis as any).Deno !== 'undefined') { + return (globalThis as any).Deno.env?.get?.(env)?.trim(); + } + return; +}; + +export const readEnvOrError = (env: string): string => { + let envValue = readEnv(env); + if (envValue === undefined) { + throw new Error(`Environment variable ${env} is not set`); + } + return envValue; +}; diff --git a/packages/mcp-server/src/stdio.ts b/packages/mcp-server/src/stdio.ts new file mode 100644 index 00000000..d902a5bb --- /dev/null +++ b/packages/mcp-server/src/stdio.ts @@ -0,0 +1,13 @@ +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { initMcpServer, newMcpServer } from './server'; +import { McpOptions } from './options'; + +export const launchStdioServer = async (options: McpOptions) => { + const server = newMcpServer(); + + initMcpServer({ server, mcpOptions: options }); + + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error('MCP Server running on stdio'); +}; diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts new file mode 100644 index 00000000..7e516de7 --- /dev/null +++ b/packages/mcp-server/src/tools.ts @@ -0,0 +1 @@ +export * from './tools/index'; diff --git a/packages/mcp-server/src/tools/accounts/origins/create-accounts-origins.ts b/packages/mcp-server/src/tools/accounts/origins/create-accounts-origins.ts new file mode 100644 index 00000000..e1775bd4 --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/origins/create-accounts-origins.ts @@ -0,0 +1,318 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.origins', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/accounts/origins', + operationId: 'create-origin', +}; + +export const tool: Tool = { + name: 'create_accounts_origins', + description: + '**Note:** This API is currently in beta. \nCreates a new origin and returns the origin object.\n', + inputSchema: { + type: 'object', + anyOf: [ + { + type: 'object', + properties: { + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['S3'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + }, + required: ['accessKey', 'bucket', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + endpoint: { + type: 'string', + description: 'Custom S3-compatible endpoint.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['S3_COMPATIBLE'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + s3ForcePathStyle: { + type: 'boolean', + description: 'Use path-style S3 URLs?', + }, + }, + required: ['accessKey', 'bucket', 'endpoint', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['CLOUDINARY_BACKUP'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + }, + required: ['accessKey', 'bucket', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + baseUrl: { + type: 'string', + description: 'Root URL for the web folder origin.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + type: { + type: 'string', + enum: ['WEB_FOLDER'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + forwardHostHeaderToOrigin: { + type: 'boolean', + description: 'Forward the Host header to origin?', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['baseUrl', 'name', 'type'], + }, + { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Display name of the origin.', + }, + type: { + type: 'string', + enum: ['WEB_PROXY'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['name', 'type'], + }, + { + type: 'object', + properties: { + bucket: { + type: 'string', + }, + clientEmail: { + type: 'string', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + privateKey: { + type: 'string', + }, + type: { + type: 'string', + enum: ['GCS'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + }, + }, + required: ['bucket', 'clientEmail', 'name', 'privateKey', 'type'], + }, + { + type: 'object', + properties: { + accountName: { + type: 'string', + }, + container: { + type: 'string', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + sasToken: { + type: 'string', + }, + type: { + type: 'string', + enum: ['AZURE_BLOB'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + }, + }, + required: ['accountName', 'container', 'name', 'sasToken', 'type'], + }, + { + type: 'object', + properties: { + baseUrl: { + type: 'string', + description: 'Akeneo instance base URL.', + }, + clientId: { + type: 'string', + description: 'Akeneo API client ID.', + }, + clientSecret: { + type: 'string', + description: 'Akeneo API client secret.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + password: { + type: 'string', + description: 'Akeneo API password.', + }, + type: { + type: 'string', + enum: ['AKENEO_PIM'], + }, + username: { + type: 'string', + description: 'Akeneo API username.', + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['baseUrl', 'clientId', 'clientSecret', 'name', 'password', 'type', 'username'], + }, + ], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const body = args as any; + return asTextContentResult(await client.accounts.origins.create(body)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/origins/delete-accounts-origins.ts b/packages/mcp-server/src/tools/accounts/origins/delete-accounts-origins.ts new file mode 100644 index 00000000..95c2ec3c --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/origins/delete-accounts-origins.ts @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.origins', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/accounts/origins/{id}', + operationId: 'delete-origin', +}; + +export const tool: Tool = { + name: 'delete_accounts_origins', + description: + '**Note:** This API is currently in beta. \nPermanently removes the origin identified by `id`. If the origin is in use by any URL‑endpoints, the API will return an error.\n', + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + }, + required: ['id'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, ...body } = args as any; + const response = await client.accounts.origins.delete(id).asResponse(); + return asTextContentResult(await response.text()); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/origins/get-accounts-origins.ts b/packages/mcp-server/src/tools/accounts/origins/get-accounts-origins.ts new file mode 100644 index 00000000..45a2d9b7 --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/origins/get-accounts-origins.ts @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.origins', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/accounts/origins/{id}', + operationId: 'get-origin', +}; + +export const tool: Tool = { + name: 'get_accounts_origins', + description: '**Note:** This API is currently in beta. \nRetrieves the origin identified by `id`.\n', + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + }, + required: ['id'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, ...body } = args as any; + return asTextContentResult(await client.accounts.origins.get(id)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/origins/list-accounts-origins.ts b/packages/mcp-server/src/tools/accounts/origins/list-accounts-origins.ts new file mode 100644 index 00000000..ed36d53b --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/origins/list-accounts-origins.ts @@ -0,0 +1,35 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.origins', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/accounts/origins', + operationId: 'list-origins', +}; + +export const tool: Tool = { + name: 'list_accounts_origins', + description: + '**Note:** This API is currently in beta. \nReturns an array of all configured origins for the current account.\n', + inputSchema: { + type: 'object', + properties: {}, + required: [], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + return asTextContentResult(await client.accounts.origins.list()); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/origins/update-accounts-origins.ts b/packages/mcp-server/src/tools/accounts/origins/update-accounts-origins.ts new file mode 100644 index 00000000..3cc2da7b --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/origins/update-accounts-origins.ts @@ -0,0 +1,360 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.origins', + operation: 'write', + tags: [], + httpMethod: 'put', + httpPath: '/v1/accounts/origins/{id}', + operationId: 'update-origin', +}; + +export const tool: Tool = { + name: 'update_accounts_origins', + description: + '**Note:** This API is currently in beta. \nUpdates the origin identified by `id` and returns the updated origin object.\n', + inputSchema: { + type: 'object', + anyOf: [ + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['S3'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + }, + required: ['id', 'accessKey', 'bucket', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + endpoint: { + type: 'string', + description: 'Custom S3-compatible endpoint.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['S3_COMPATIBLE'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + s3ForcePathStyle: { + type: 'boolean', + description: 'Use path-style S3 URLs?', + }, + }, + required: ['id', 'accessKey', 'bucket', 'endpoint', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + accessKey: { + type: 'string', + description: 'Access key for the bucket.', + }, + bucket: { + type: 'string', + description: 'S3 bucket name.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + secretKey: { + type: 'string', + description: 'Secret key for the bucket.', + }, + type: { + type: 'string', + enum: ['CLOUDINARY_BACKUP'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + description: 'Path prefix inside the bucket.', + }, + }, + required: ['id', 'accessKey', 'bucket', 'name', 'secretKey', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + baseUrl: { + type: 'string', + description: 'Root URL for the web folder origin.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + type: { + type: 'string', + enum: ['WEB_FOLDER'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + forwardHostHeaderToOrigin: { + type: 'boolean', + description: 'Forward the Host header to origin?', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['id', 'baseUrl', 'name', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + type: { + type: 'string', + enum: ['WEB_PROXY'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['id', 'name', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + bucket: { + type: 'string', + }, + clientEmail: { + type: 'string', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + privateKey: { + type: 'string', + }, + type: { + type: 'string', + enum: ['GCS'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + }, + }, + required: ['id', 'bucket', 'clientEmail', 'name', 'privateKey', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + accountName: { + type: 'string', + }, + container: { + type: 'string', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + sasToken: { + type: 'string', + }, + type: { + type: 'string', + enum: ['AZURE_BLOB'], + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + prefix: { + type: 'string', + }, + }, + required: ['id', 'accountName', 'container', 'name', 'sasToken', 'type'], + }, + { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + baseUrl: { + type: 'string', + description: 'Akeneo instance base URL.', + }, + clientId: { + type: 'string', + description: 'Akeneo API client ID.', + }, + clientSecret: { + type: 'string', + description: 'Akeneo API client secret.', + }, + name: { + type: 'string', + description: 'Display name of the origin.', + }, + password: { + type: 'string', + description: 'Akeneo API password.', + }, + type: { + type: 'string', + enum: ['AKENEO_PIM'], + }, + username: { + type: 'string', + description: 'Akeneo API username.', + }, + baseUrlForCanonicalHeader: { + type: 'string', + description: 'URL used in the Canonical header (if enabled).', + }, + includeCanonicalHeader: { + type: 'boolean', + description: 'Whether to send a Canonical header.', + }, + }, + required: ['id', 'baseUrl', 'clientId', 'clientSecret', 'name', 'password', 'type', 'username'], + }, + ], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, ...body } = args as any; + return asTextContentResult(await client.accounts.origins.update(id, body)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/url-endpoints/create-accounts-url-endpoints.ts b/packages/mcp-server/src/tools/accounts/url-endpoints/create-accounts-url-endpoints.ts new file mode 100644 index 00000000..b7457fcf --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/url-endpoints/create-accounts-url-endpoints.ts @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.urlEndpoints', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/accounts/url-endpoints', + operationId: 'create-url-endpoint', +}; + +export const tool: Tool = { + name: 'create_accounts_url_endpoints', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n**Note:** This API is currently in beta. \nCreates a new URL‑endpoint and returns the resulting object.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/url_endpoint_response',\n $defs: {\n url_endpoint_response: {\n type: 'object',\n title: 'URL‑endpoint Response',\n description: 'URL‑endpoint object as returned by the API.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.'\n },\n description: {\n type: 'string',\n description: 'Description of the URL endpoint.'\n },\n origins: {\n type: 'array',\n description: 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.',\n items: {\n type: 'string',\n description: 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.'\n }\n },\n urlPrefix: {\n type: 'string',\n description: 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).'\n },\n urlRewriter: {\n anyOf: [ {\n type: 'object',\n title: 'Cloudinary URL Rewriter',\n properties: {\n preserveAssetDeliveryTypes: {\n type: 'boolean',\n description: 'Whether to preserve `/` in the rewritten URL.'\n },\n type: {\n type: 'string',\n enum: [ 'CLOUDINARY'\n ]\n }\n },\n required: [ 'preserveAssetDeliveryTypes',\n 'type'\n ]\n },\n {\n type: 'object',\n title: 'Imgix URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'IMGIX'\n ]\n }\n },\n required: [ 'type'\n ]\n },\n {\n type: 'object',\n title: 'Akamai URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'AKAMAI'\n ]\n }\n },\n required: [ 'type'\n ]\n }\n ],\n description: 'Configuration for third-party URL rewriting.'\n }\n },\n required: [ 'id',\n 'description',\n 'origins',\n 'urlPrefix'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + description: { + type: 'string', + description: 'Description of the URL endpoint.', + }, + origins: { + type: 'array', + description: + 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.', + items: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + }, + urlPrefix: { + type: 'string', + description: + 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).', + }, + urlRewriter: { + anyOf: [ + { + type: 'object', + title: 'Cloudinary URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['CLOUDINARY'], + }, + preserveAssetDeliveryTypes: { + type: 'boolean', + description: 'Whether to preserve `/` in the rewritten URL.', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Imgix URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['IMGIX'], + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Akamai URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['AKAMAI'], + }, + }, + required: ['type'], + }, + ], + description: 'Configuration for third-party URL rewriting.', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['description'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.accounts.urlEndpoints.create(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/url-endpoints/delete-accounts-url-endpoints.ts b/packages/mcp-server/src/tools/accounts/url-endpoints/delete-accounts-url-endpoints.ts new file mode 100644 index 00000000..da7f6134 --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/url-endpoints/delete-accounts-url-endpoints.ts @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.urlEndpoints', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/accounts/url-endpoints/{id}', + operationId: 'delete-url-endpoint', +}; + +export const tool: Tool = { + name: 'delete_accounts_url_endpoints', + description: + '**Note:** This API is currently in beta. \nDeletes the URL‑endpoint identified by `id`. You cannot delete the default URL‑endpoint created by ImageKit during account creation.\n', + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.', + }, + }, + required: ['id'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, ...body } = args as any; + const response = await client.accounts.urlEndpoints.delete(id).asResponse(); + return asTextContentResult(await response.text()); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/url-endpoints/get-accounts-url-endpoints.ts b/packages/mcp-server/src/tools/accounts/url-endpoints/get-accounts-url-endpoints.ts new file mode 100644 index 00000000..f21f9c8b --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/url-endpoints/get-accounts-url-endpoints.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.urlEndpoints', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/accounts/url-endpoints/{id}', + operationId: 'get-url-endpoint', +}; + +export const tool: Tool = { + name: 'get_accounts_url_endpoints', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n**Note:** This API is currently in beta. \nRetrieves the URL‑endpoint identified by `id`.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/url_endpoint_response',\n $defs: {\n url_endpoint_response: {\n type: 'object',\n title: 'URL‑endpoint Response',\n description: 'URL‑endpoint object as returned by the API.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.'\n },\n description: {\n type: 'string',\n description: 'Description of the URL endpoint.'\n },\n origins: {\n type: 'array',\n description: 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.',\n items: {\n type: 'string',\n description: 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.'\n }\n },\n urlPrefix: {\n type: 'string',\n description: 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).'\n },\n urlRewriter: {\n anyOf: [ {\n type: 'object',\n title: 'Cloudinary URL Rewriter',\n properties: {\n preserveAssetDeliveryTypes: {\n type: 'boolean',\n description: 'Whether to preserve `/` in the rewritten URL.'\n },\n type: {\n type: 'string',\n enum: [ 'CLOUDINARY'\n ]\n }\n },\n required: [ 'preserveAssetDeliveryTypes',\n 'type'\n ]\n },\n {\n type: 'object',\n title: 'Imgix URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'IMGIX'\n ]\n }\n },\n required: [ 'type'\n ]\n },\n {\n type: 'object',\n title: 'Akamai URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'AKAMAI'\n ]\n }\n },\n required: [ 'type'\n ]\n }\n ],\n description: 'Configuration for third-party URL rewriting.'\n }\n },\n required: [ 'id',\n 'description',\n 'origins',\n 'urlPrefix'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['id'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.accounts.urlEndpoints.get(id))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/url-endpoints/list-accounts-url-endpoints.ts b/packages/mcp-server/src/tools/accounts/url-endpoints/list-accounts-url-endpoints.ts new file mode 100644 index 00000000..f33b762b --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/url-endpoints/list-accounts-url-endpoints.ts @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.urlEndpoints', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/accounts/url-endpoints', + operationId: 'list-url-endpoints', +}; + +export const tool: Tool = { + name: 'list_accounts_url_endpoints', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n**Note:** This API is currently in beta. \nReturns an array of all URL‑endpoints configured including the default URL-endpoint generated by ImageKit during account creation.\n\n\n# Response Schema\n```json\n{\n type: 'array',\n items: {\n $ref: '#/$defs/url_endpoint_response'\n },\n $defs: {\n url_endpoint_response: {\n type: 'object',\n title: 'URL‑endpoint Response',\n description: 'URL‑endpoint object as returned by the API.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.'\n },\n description: {\n type: 'string',\n description: 'Description of the URL endpoint.'\n },\n origins: {\n type: 'array',\n description: 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.',\n items: {\n type: 'string',\n description: 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.'\n }\n },\n urlPrefix: {\n type: 'string',\n description: 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).'\n },\n urlRewriter: {\n anyOf: [ {\n type: 'object',\n title: 'Cloudinary URL Rewriter',\n properties: {\n preserveAssetDeliveryTypes: {\n type: 'boolean',\n description: 'Whether to preserve `/` in the rewritten URL.'\n },\n type: {\n type: 'string',\n enum: [ 'CLOUDINARY'\n ]\n }\n },\n required: [ 'preserveAssetDeliveryTypes',\n 'type'\n ]\n },\n {\n type: 'object',\n title: 'Imgix URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'IMGIX'\n ]\n }\n },\n required: [ 'type'\n ]\n },\n {\n type: 'object',\n title: 'Akamai URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'AKAMAI'\n ]\n }\n },\n required: [ 'type'\n ]\n }\n ],\n description: 'Configuration for third-party URL rewriting.'\n }\n },\n required: [ 'id',\n 'description',\n 'origins',\n 'urlPrefix'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: [], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.accounts.urlEndpoints.list())); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/url-endpoints/update-accounts-url-endpoints.ts b/packages/mcp-server/src/tools/accounts/url-endpoints/update-accounts-url-endpoints.ts new file mode 100644 index 00000000..0ff252f4 --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/url-endpoints/update-accounts-url-endpoints.ts @@ -0,0 +1,112 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.urlEndpoints', + operation: 'write', + tags: [], + httpMethod: 'put', + httpPath: '/v1/accounts/url-endpoints/{id}', + operationId: 'update-url-endpoint', +}; + +export const tool: Tool = { + name: 'update_accounts_url_endpoints', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n**Note:** This API is currently in beta. \nUpdates the URL‑endpoint identified by `id` and returns the updated object.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/url_endpoint_response',\n $defs: {\n url_endpoint_response: {\n type: 'object',\n title: 'URL‑endpoint Response',\n description: 'URL‑endpoint object as returned by the API.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.'\n },\n description: {\n type: 'string',\n description: 'Description of the URL endpoint.'\n },\n origins: {\n type: 'array',\n description: 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.',\n items: {\n type: 'string',\n description: 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.'\n }\n },\n urlPrefix: {\n type: 'string',\n description: 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).'\n },\n urlRewriter: {\n anyOf: [ {\n type: 'object',\n title: 'Cloudinary URL Rewriter',\n properties: {\n preserveAssetDeliveryTypes: {\n type: 'boolean',\n description: 'Whether to preserve `/` in the rewritten URL.'\n },\n type: {\n type: 'string',\n enum: [ 'CLOUDINARY'\n ]\n }\n },\n required: [ 'preserveAssetDeliveryTypes',\n 'type'\n ]\n },\n {\n type: 'object',\n title: 'Imgix URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'IMGIX'\n ]\n }\n },\n required: [ 'type'\n ]\n },\n {\n type: 'object',\n title: 'Akamai URL Rewriter',\n properties: {\n type: {\n type: 'string',\n enum: [ 'AKAMAI'\n ]\n }\n },\n required: [ 'type'\n ]\n }\n ],\n description: 'Configuration for third-party URL rewriting.'\n }\n },\n required: [ 'id',\n 'description',\n 'origins',\n 'urlPrefix'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + description: + 'Unique identifier for the URL-endpoint. This is generated by ImageKit when you create a new URL-endpoint. For the default URL-endpoint, this is always `default`.', + }, + description: { + type: 'string', + description: 'Description of the URL endpoint.', + }, + origins: { + type: 'array', + description: + 'Ordered list of origin IDs to try when the file isn’t in the Media Library; ImageKit checks them in the sequence provided. Origin must be created before it can be used in a URL endpoint.', + items: { + type: 'string', + description: + 'Unique identifier for the origin. This is generated by ImageKit when you create a new origin.', + }, + }, + urlPrefix: { + type: 'string', + description: + 'Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint).', + }, + urlRewriter: { + anyOf: [ + { + type: 'object', + title: 'Cloudinary URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['CLOUDINARY'], + }, + preserveAssetDeliveryTypes: { + type: 'boolean', + description: 'Whether to preserve `/` in the rewritten URL.', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Imgix URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['IMGIX'], + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Akamai URL Rewriter', + properties: { + type: { + type: 'string', + enum: ['AKAMAI'], + }, + }, + required: ['type'], + }, + ], + description: 'Configuration for third-party URL rewriting.', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['id', 'description'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, jq_filter, ...body } = args as any; + return asTextContentResult( + await maybeFilter(jq_filter, await client.accounts.urlEndpoints.update(id, body)), + ); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/accounts/usage/get-accounts-usage.ts b/packages/mcp-server/src/tools/accounts/usage/get-accounts-usage.ts new file mode 100644 index 00000000..5504a690 --- /dev/null +++ b/packages/mcp-server/src/tools/accounts/usage/get-accounts-usage.ts @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'accounts.usage', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/accounts/usage', + operationId: 'get-usage', +}; + +export const tool: Tool = { + name: 'get_accounts_usage', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nGet the account usage information between two dates. Note that the API response includes data from the start date while excluding data from the end date. In other words, the data covers the period starting from the specified start date up to, but not including, the end date.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n bandwidthBytes: {\n type: 'integer',\n description: 'Amount of bandwidth used in bytes.'\n },\n extensionUnitsCount: {\n type: 'integer',\n description: 'Number of extension units used.'\n },\n mediaLibraryStorageBytes: {\n type: 'integer',\n description: 'Storage used by media library in bytes.'\n },\n originalCacheStorageBytes: {\n type: 'integer',\n description: 'Storage used by the original cache in bytes.'\n },\n videoProcessingUnitsCount: {\n type: 'integer',\n description: 'Number of video processing units used.'\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + endDate: { + type: 'string', + description: + 'Specify a `endDate` in `YYYY-MM-DD` format. It should be after the `startDate`. The difference between `startDate` and `endDate` should be less than 90 days.', + format: 'date', + }, + startDate: { + type: 'string', + description: + 'Specify a `startDate` in `YYYY-MM-DD` format. It should be before the `endDate`. The difference between `startDate` and `endDate` should be less than 90 days.', + format: 'date', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['endDate', 'startDate'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.accounts.usage.get(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/assets/list-assets.ts b/packages/mcp-server/src/tools/assets/list-assets.ts new file mode 100644 index 00000000..7ed71596 --- /dev/null +++ b/packages/mcp-server/src/tools/assets/list-assets.ts @@ -0,0 +1,94 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'assets', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files', + operationId: 'list-and-search-assets', +}; + +export const tool: Tool = { + name: 'list_assets', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API can list all the uploaded files and folders in your ImageKit.io media library. In addition, you can fine-tune your query by specifying various filters by generating a query string in a Lucene-like syntax and provide this generated string as the value of the `searchQuery`.\n\n\n# Response Schema\n```json\n{\n type: 'array',\n items: {\n anyOf: [ {\n $ref: '#/$defs/file'\n },\n {\n $ref: '#/$defs/folder'\n }\n ],\n description: 'Object containing details of a file or file version.'\n },\n $defs: {\n file: {\n type: 'object',\n title: 'File & File Version',\n description: 'Object containing details of a file or file version.',\n properties: {\n AITags: {\n type: 'array',\n description: 'An array of tags assigned to the file by auto tagging.\\n',\n items: {\n type: 'object',\n properties: {\n confidence: {\n type: 'number',\n description: 'Confidence score of the tag.'\n },\n name: {\n type: 'string',\n description: 'Name of the tag.'\n },\n source: {\n type: 'string',\n description: 'Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`.'\n }\n }\n }\n },\n createdAt: {\n type: 'string',\n description: 'Date and time when the file was uploaded. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n customCoordinates: {\n type: 'string',\n description: 'An string with custom coordinates of the file.\\n'\n },\n customMetadata: {\n type: 'object',\n description: 'An object with custom metadata for the file.\\n',\n additionalProperties: true\n },\n description: {\n type: 'string',\n description: 'Optional text to describe the contents of the file. Can be set by the user or the ai-auto-description extension.\\n'\n },\n fileId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n filePath: {\n type: 'string',\n description: 'Path of the file. This is the path you would use in the URL to access the file. For example, if the file is at the root of the media library, the path will be `/file.jpg`. If the file is inside a folder named `images`, the path will be `/images/file.jpg`.\\n'\n },\n fileType: {\n type: 'string',\n description: 'Type of the file. Possible values are `image`, `non-image`.\\n'\n },\n hasAlpha: {\n type: 'boolean',\n description: 'Specifies if the image has an alpha channel.\\n'\n },\n height: {\n type: 'number',\n description: 'Height of the file.\\n'\n },\n isPrivateFile: {\n type: 'boolean',\n description: 'Specifies if the file is private or not.\\n'\n },\n isPublished: {\n type: 'boolean',\n description: 'Specifies if the file is published or not.\\n'\n },\n mime: {\n type: 'string',\n description: 'MIME type of the file.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n size: {\n type: 'number',\n description: 'Size of the file in bytes.\\n'\n },\n tags: {\n type: 'array',\n description: 'An array of tags assigned to the file. Tags are used to search files in the media library.\\n',\n items: {\n type: 'string'\n }\n },\n thumbnail: {\n type: 'string',\n description: 'URL of the thumbnail image. This URL is used to access the thumbnail image of the file in the media library.\\n'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'file',\n 'file-version'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the file was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n url: {\n type: 'string',\n description: 'URL of the file.\\n'\n },\n versionInfo: {\n type: 'object',\n description: 'An object with details of the file version.\\n',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the file version.'\n },\n name: {\n type: 'string',\n description: 'Name of the file version.'\n }\n }\n },\n width: {\n type: 'number',\n description: 'Width of the file.\\n'\n }\n }\n },\n folder: {\n type: 'object',\n title: 'Folder',\n properties: {\n createdAt: {\n type: 'string',\n description: 'Date and time when the folder was created. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n folderId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n folderPath: {\n type: 'string',\n description: 'Path of the folder. This is the path you would use in the URL to access the folder. For example, if the folder is at the root of the media library, the path will be /folder. If the folder is inside another folder named images, the path will be /images/folder.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'folder'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the folder was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileType: { + type: 'string', + description: + 'Filter results by file type.\n\n- `all` — include all file types \n- `image` — include only image files \n- `non-image` — include only non-image files (e.g., JS, CSS, video)', + enum: ['all', 'image', 'non-image'], + }, + limit: { + type: 'integer', + description: 'The maximum number of results to return in response.\n', + }, + path: { + type: 'string', + description: + 'Folder path if you want to limit the search within a specific folder. For example, `/sales-banner/` will only search in folder sales-banner.\n\nNote : If your use case involves searching within a folder as well as its subfolders, you can use `path` parameter in `searchQuery` with appropriate operator.\nCheckout [Supported parameters](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#supported-parameters) for more information.\n', + }, + searchQuery: { + type: 'string', + description: + 'Query string in a Lucene-like query language e.g. `createdAt > "7d"`.\n\nNote : When the searchQuery parameter is present, the following query parameters will have no effect on the result:\n\n1. `tags`\n2. `type`\n3. `name`\n\n[Learn more](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#advanced-search-queries) from examples.\n', + }, + skip: { + type: 'integer', + description: 'The number of results to skip before returning results.\n', + }, + sort: { + type: 'string', + description: 'Sort the results by one of the supported fields in ascending or descending order.', + enum: [ + 'ASC_NAME', + 'DESC_NAME', + 'ASC_CREATED', + 'DESC_CREATED', + 'ASC_UPDATED', + 'DESC_UPDATED', + 'ASC_HEIGHT', + 'DESC_HEIGHT', + 'ASC_WIDTH', + 'DESC_WIDTH', + 'ASC_SIZE', + 'DESC_SIZE', + 'ASC_RELEVANCE', + 'DESC_RELEVANCE', + ], + }, + type: { + type: 'string', + description: + 'Filter results by asset type.\n\n- `file` — returns only files \n- `file-version` — returns specific file versions \n- `folder` — returns only folders \n- `all` — returns both files and folders (excludes `file-version`)', + enum: ['file', 'file-version', 'folder', 'all'], + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: [], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.assets.list(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/beta/v2/files/upload-v2-beta-files.ts b/packages/mcp-server/src/tools/beta/v2/files/upload-v2-beta-files.ts new file mode 100644 index 00000000..fcb48cae --- /dev/null +++ b/packages/mcp-server/src/tools/beta/v2/files/upload-v2-beta-files.ts @@ -0,0 +1,315 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'beta.v2.files', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/api/v2/files/upload', + operationId: 'upload-file-v2', +}; + +export const tool: Tool = { + name: 'upload_v2_beta_files', + description: + 'The V2 API enhances security by verifying the entire payload using JWT. This API is in beta.\n\nImageKit.io allows you to upload files directly from both the server and client sides. For server-side uploads, private API key authentication is used. For client-side uploads, generate a one-time `token` from your secure backend using private API. [Learn more](/docs/api-reference/upload-file/upload-file-v2#how-to-implement-secure-client-side-file-upload) about how to implement secure client-side file upload.\n\n**File size limit** \\\nOn the free plan, the maximum upload file sizes are 20MB for images, audio, and raw files, and 100MB for videos. On the paid plan, these limits increase to 40MB for images, audio, and raw files, and 2GB for videos. These limits can be further increased with higher-tier plans.\n\n**Version limit** \\\nA file can have a maximum of 100 versions.\n\n**Demo applications**\n\n- A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, and more.\n- [Quick start guides](/docs/quick-start-guides) for various frameworks and technologies.\n', + inputSchema: { + type: 'object', + properties: { + file: { + type: 'string', + description: + 'The API accepts any of the following:\n\n- **Binary data** – send the raw bytes as `multipart/form-data`.\n- **HTTP / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can fetch.\n- **Base64 string** – the file encoded as a Base64 data URI or plain Base64.\n\nWhen supplying a URL, the server must receive the response headers within 8 seconds; otherwise the request fails with 400 Bad Request.\n', + }, + fileName: { + type: 'string', + description: 'The name with which the file has to be uploaded.\n', + }, + token: { + type: 'string', + description: + "This is the client-generated JSON Web Token (JWT). The ImageKit.io server uses it to authenticate and check that the upload request parameters have not been tampered with after the token has been generated. Learn how to create the token on the page below. This field is only required for authentication when uploading a file from the client side.\n\n\n**Note**: Sending a JWT that has been used in the past will result in a validation error. Even if your previous request resulted in an error, you should always send a new token.\n\n\n**⚠️Warning**: JWT must be generated on the server-side because it is generated using your account's private API key. This field is required for authentication when uploading a file from the client-side.\n", + }, + checks: { + type: 'string', + description: + 'Server-side checks to run on the asset.\nRead more about [Upload API checks](/docs/api-reference/upload-file/upload-file-v2#upload-api-checks).\n', + }, + customCoordinates: { + type: 'string', + description: + 'Define an important area in the image. This is only relevant for image type files.\n\n - To be passed as a string with the x and y coordinates of the top-left corner, and width and height of the area of interest in the format `x,y,width,height`. For example - `10,10,100,100`\n - Can be used with fo-customtransformation.\n - If this field is not specified and the file is overwritten, then customCoordinates will be removed.\n', + }, + customMetadata: { + type: 'object', + description: + 'JSON key-value pairs to associate with the asset. Create the custom metadata fields before setting these values.\n', + additionalProperties: true, + }, + description: { + type: 'string', + description: 'Optional text to describe the contents of the file.\n', + }, + extensions: { + $ref: '#/$defs/extensions', + }, + folder: { + type: 'string', + description: + "The folder path in which the image has to be uploaded. If the folder(s) didn't exist before, a new folder(s) is created. Using multiple `/` creates a nested folder.\n", + }, + isPrivateFile: { + type: 'boolean', + description: + 'Whether to mark the file as private or not.\n\nIf `true`, the file is marked as private and is accessible only using named transformation or signed URL.\n', + }, + isPublished: { + type: 'boolean', + description: + 'Whether to upload file as published or not.\n\nIf `false`, the file is marked as unpublished, which restricts access to the file only via the media library. Files in draft or unpublished state can only be publicly accessed after being published.\n\nThe option to upload in draft state is only available in custom enterprise pricing plans.\n', + }, + overwriteAITags: { + type: 'boolean', + description: + 'If set to `true` and a file already exists at the exact location, its AITags will be removed. Set `overwriteAITags` to `false` to preserve AITags.\n', + }, + overwriteCustomMetadata: { + type: 'boolean', + description: + 'If the request does not have `customMetadata`, and a file already exists at the exact location, existing customMetadata will be removed.\n', + }, + overwriteFile: { + type: 'boolean', + description: + 'If `false` and `useUniqueFileName` is also `false`, and a file already exists at the exact location, upload API will return an error immediately.\n', + }, + overwriteTags: { + type: 'boolean', + description: + 'If the request does not have `tags`, and a file already exists at the exact location, existing tags will be removed.\n', + }, + responseFields: { + type: 'array', + description: 'Array of response field keys to include in the API response body.\n', + items: { + type: 'string', + enum: [ + 'tags', + 'customCoordinates', + 'isPrivateFile', + 'embeddedMetadata', + 'isPublished', + 'customMetadata', + 'metadata', + ], + }, + }, + tags: { + type: 'array', + description: + 'Set the tags while uploading the file.\nProvide an array of tag strings (e.g. `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must not exceed 500, and the `%` character is not allowed.\nIf this field is not specified and the file is overwritten, the existing tags will be removed.\n', + items: { + type: 'string', + }, + }, + transformation: { + type: 'object', + description: + "Configure pre-processing (`pre`) and post-processing (`post`) transformations.\n\n- `pre` — applied before the file is uploaded to the Media Library. \n Useful for reducing file size or applying basic optimizations upfront (e.g., resize, compress).\n\n- `post` — applied immediately after upload. \n Ideal for generating transformed versions (like video encodes or thumbnails) in advance, so they're ready for delivery without delay.\n\nYou can mix and match any combination of post-processing types.\n", + properties: { + post: { + type: 'array', + description: + 'List of transformations to apply *after* the file is uploaded. \nEach item must match one of the following types:\n`transformation`, `gif-to-video`, `thumbnail`, `abs`.\n', + items: { + anyOf: [ + { + type: 'object', + title: 'Simple post-transformation', + properties: { + type: { + type: 'string', + description: 'Transformation type.', + enum: ['transformation'], + }, + value: { + type: 'string', + description: + 'Transformation string (e.g. `w-200,h-200`). \nSame syntax as ImageKit URL-based transformations.\n', + }, + }, + required: ['type', 'value'], + }, + { + type: 'object', + title: 'Convert GIF to video', + properties: { + type: { + type: 'string', + description: 'Converts an animated GIF into an MP4.', + enum: ['gif-to-video'], + }, + value: { + type: 'string', + description: + 'Optional transformation string to apply to the output video. \n**Example**: `q-80`\n', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Generate a thumbnail', + properties: { + type: { + type: 'string', + description: 'Generates a thumbnail image.', + enum: ['thumbnail'], + }, + value: { + type: 'string', + description: 'Optional transformation string. \n**Example**: `w-150,h-150`\n', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Adaptive Bitrate Streaming', + properties: { + protocol: { + type: 'string', + description: 'Streaming protocol to use (`hls` or `dash`).', + enum: ['hls', 'dash'], + }, + type: { + type: 'string', + description: 'Adaptive Bitrate Streaming (ABS) setup.', + enum: ['abs'], + }, + value: { + type: 'string', + description: + 'List of different representations you want to create separated by an underscore.\n', + }, + }, + required: ['protocol', 'type', 'value'], + }, + ], + }, + }, + pre: { + type: 'string', + description: + 'Transformation string to apply before uploading the file to the Media Library. Useful for optimizing files at ingestion.\n', + }, + }, + }, + useUniqueFileName: { + type: 'boolean', + description: + 'Whether to use a unique filename for this file or not.\n\nIf `true`, ImageKit.io will add a unique suffix to the filename parameter to get a unique filename.\n\nIf `false`, then the image is uploaded with the provided filename parameter, and any existing file with the same name is replaced.\n', + }, + webhookUrl: { + type: 'string', + description: + 'The final status of extensions after they have completed execution will be delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) about the webhook payload structure.\n', + }, + }, + required: ['file', 'fileName'], + $defs: { + extensions: { + type: 'array', + title: 'Extensions Array', + description: + 'Array of extensions to be applied to the asset. Each extension can be configured with specific parameters based on the extension type.\n', + items: { + anyOf: [ + { + type: 'object', + title: 'Remove background', + properties: { + name: { + type: 'string', + description: 'Specifies the background removal extension.', + enum: ['remove-bg'], + }, + options: { + type: 'object', + properties: { + add_shadow: { + type: 'boolean', + description: + 'Whether to add an artificial shadow to the result. Default is false. Note: Adding shadows is currently only supported for car photos.\n', + }, + bg_color: { + type: 'string', + description: + 'Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or color name (e.g., "green"). If this parameter is set, `bg_image_url` must be empty.\n', + }, + bg_image_url: { + type: 'string', + description: + 'Sets a background image from a URL. If this parameter is set, `bg_color` must be empty.\n', + }, + semitransparency: { + type: 'boolean', + description: + 'Allows semi-transparent regions in the result. Default is true. Note: Semitransparency is currently only supported for car windows.\n', + }, + }, + }, + }, + required: ['name'], + }, + { + type: 'object', + title: 'Auto tagging', + properties: { + maxTags: { + type: 'integer', + description: 'Maximum number of tags to attach to the asset.', + }, + minConfidence: { + type: 'integer', + description: 'Minimum confidence level for tags to be considered valid.', + }, + name: { + type: 'string', + description: 'Specifies the auto-tagging extension used.', + enum: ['google-auto-tagging', 'aws-auto-tagging'], + }, + }, + required: ['maxTags', 'minConfidence', 'name'], + }, + { + type: 'object', + title: 'Auto description', + properties: { + name: { + type: 'string', + description: 'Specifies the auto description extension.', + enum: ['ai-auto-description'], + }, + }, + required: ['name'], + }, + ], + }, + }, + }, + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const body = args as any; + return asTextContentResult(await client.beta.v2.files.upload(body)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/cache/invalidation/create-cache-invalidation.ts b/packages/mcp-server/src/tools/cache/invalidation/create-cache-invalidation.ts new file mode 100644 index 00000000..f3eaf25b --- /dev/null +++ b/packages/mcp-server/src/tools/cache/invalidation/create-cache-invalidation.ts @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'cache.invalidation', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/purge', + operationId: 'purge-cache', +}; + +export const tool: Tool = { + name: 'create_cache_invalidation', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API will purge CDN cache and ImageKit.io's internal cache for a file. Note: Purge cache is an asynchronous process and it may take some time to reflect the changes.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n requestId: {\n type: 'string',\n description: 'Unique identifier of the purge request. This can be used to check the status of the purge request.\\n'\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'The full URL of the file to be purged.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['url'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.cache.invalidation.create(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/cache/invalidation/get-cache-invalidation.ts b/packages/mcp-server/src/tools/cache/invalidation/get-cache-invalidation.ts new file mode 100644 index 00000000..2cb17484 --- /dev/null +++ b/packages/mcp-server/src/tools/cache/invalidation/get-cache-invalidation.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'cache.invalidation', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/purge/{requestId}', + operationId: 'purge-status', +}; + +export const tool: Tool = { + name: 'get_cache_invalidation', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns the status of a purge cache request.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n status: {\n type: 'string',\n description: 'Status of the purge request.',\n enum: [ 'Pending',\n 'Completed'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + requestId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['requestId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { requestId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.cache.invalidation.get(requestId))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/custom-metadata-fields/create-custom-metadata-fields.ts b/packages/mcp-server/src/tools/custom-metadata-fields/create-custom-metadata-fields.ts new file mode 100644 index 00000000..7cd2d2e9 --- /dev/null +++ b/packages/mcp-server/src/tools/custom-metadata-fields/create-custom-metadata-fields.ts @@ -0,0 +1,154 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'customMetadataFields', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/customMetadataFields', + operationId: 'create-new-field', +}; + +export const tool: Tool = { + name: 'create_custom_metadata_fields', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API creates a new custom metadata field. Once a custom metadata field is created either through this API or using the dashboard UI, its value can be set on the assets. The value of a field for an asset can be set using the media library UI or programmatically through upload or update assets API.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/custom_metadata_field',\n $defs: {\n custom_metadata_field: {\n type: 'object',\n description: 'Object containing details of a custom metadata field.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the custom metadata field. Use this to update the field.'\n },\n label: {\n type: 'string',\n description: 'Human readable name of the custom metadata field. This name is displayed as form field label to the users while setting field value on the asset in the media library UI.\\n'\n },\n name: {\n type: 'string',\n description: 'API name of the custom metadata field. This becomes the key while setting `customMetadata` (key-value object) for an asset using upload or update API.\\n'\n },\n schema: {\n type: 'object',\n description: 'An object that describes the rules for the custom metadata field value.',\n properties: {\n type: {\n type: 'string',\n description: 'Type of the custom metadata field.',\n enum: [ 'Text',\n 'Textarea',\n 'Number',\n 'Date',\n 'Boolean',\n 'SingleSelect',\n 'MultiSelect'\n ]\n },\n defaultValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n },\n {\n type: 'array',\n title: 'Mixed',\n description: 'Default value should be of type array when custom metadata field type is set to `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n ],\n description: 'The default value for this custom metadata field. Date type of default value depends on the field type.\\n'\n },\n isValueRequired: {\n type: 'boolean',\n description: 'Specifies if the this custom metadata field is required or not.\\n'\n },\n maxLength: {\n type: 'number',\n description: 'Maximum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n maxValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Maximum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n minLength: {\n type: 'number',\n description: 'Minimum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n minValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Minimum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n selectOptions: {\n type: 'array',\n description: 'An array of allowed values when field type is `SingleSelect` or `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n },\n required: [ 'type'\n ]\n }\n },\n required: [ 'id',\n 'label',\n 'name',\n 'schema'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + label: { + type: 'string', + description: + 'Human readable name of the custom metadata field. This should be unique across all non deleted custom metadata fields. This name is displayed as form field label to the users while setting field value on an asset in the media library UI.', + }, + name: { + type: 'string', + description: + 'API name of the custom metadata field. This should be unique across all (including deleted) custom metadata fields.', + }, + schema: { + type: 'object', + properties: { + type: { + type: 'string', + description: 'Type of the custom metadata field.', + enum: ['Text', 'Textarea', 'Number', 'Date', 'Boolean', 'SingleSelect', 'MultiSelect'], + }, + defaultValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + { + type: 'array', + title: 'Mixed', + description: + 'Default value should be of type array when custom metadata field type is set to `MultiSelect`.\n', + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + ], + description: + 'The default value for this custom metadata field. This property is only required if `isValueRequired` property is set to `true`. The value should match the `type` of custom metadata field.\n', + }, + isValueRequired: { + type: 'boolean', + description: + 'Sets this custom metadata field as required. Setting custom metadata fields on an asset will throw error if the value for all required fields are not present in upload or update asset API request body.\n', + }, + maxLength: { + type: 'number', + description: + 'Maximum length of string. Only set this property if `type` is set to `Text` or `Textarea`.\n', + }, + maxValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + description: + 'Maximum value of the field. Only set this property if field type is `Date` or `Number`. For `Date` type field, set the minimum date in ISO8601 string format. For `Number` type field, set the minimum numeric value.\n', + }, + minLength: { + type: 'number', + description: + 'Minimum length of string. Only set this property if `type` is set to `Text` or `Textarea`.\n', + }, + minValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + description: + 'Minimum value of the field. Only set this property if field type is `Date` or `Number`. For `Date` type field, set the minimum date in ISO8601 string format. For `Number` type field, set the minimum numeric value.\n', + }, + selectOptions: { + type: 'array', + description: + 'An array of allowed values. This property is only required if `type` property is set to `SingleSelect` or `MultiSelect`.\n', + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + }, + required: ['type'], + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['label', 'name', 'schema'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.customMetadataFields.create(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/custom-metadata-fields/delete-custom-metadata-fields.ts b/packages/mcp-server/src/tools/custom-metadata-fields/delete-custom-metadata-fields.ts new file mode 100644 index 00000000..35adacb2 --- /dev/null +++ b/packages/mcp-server/src/tools/custom-metadata-fields/delete-custom-metadata-fields.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'customMetadataFields', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/customMetadataFields/{id}', + operationId: 'delete-a-field', +}; + +export const tool: Tool = { + name: 'delete_custom_metadata_fields', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API deletes a custom metadata field. Even after deleting a custom metadata field, you cannot create any new custom metadata field with the same name.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['id'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.customMetadataFields.delete(id))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/custom-metadata-fields/list-custom-metadata-fields.ts b/packages/mcp-server/src/tools/custom-metadata-fields/list-custom-metadata-fields.ts new file mode 100644 index 00000000..d85c214f --- /dev/null +++ b/packages/mcp-server/src/tools/custom-metadata-fields/list-custom-metadata-fields.ts @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'customMetadataFields', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/customMetadataFields', + operationId: 'list-all-fields', +}; + +export const tool: Tool = { + name: 'list_custom_metadata_fields', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns the array of created custom metadata field objects. By default the API returns only non deleted field objects, but you can include deleted fields in the API response.\n\n# Response Schema\n```json\n{\n type: 'array',\n items: {\n $ref: '#/$defs/custom_metadata_field'\n },\n $defs: {\n custom_metadata_field: {\n type: 'object',\n description: 'Object containing details of a custom metadata field.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the custom metadata field. Use this to update the field.'\n },\n label: {\n type: 'string',\n description: 'Human readable name of the custom metadata field. This name is displayed as form field label to the users while setting field value on the asset in the media library UI.\\n'\n },\n name: {\n type: 'string',\n description: 'API name of the custom metadata field. This becomes the key while setting `customMetadata` (key-value object) for an asset using upload or update API.\\n'\n },\n schema: {\n type: 'object',\n description: 'An object that describes the rules for the custom metadata field value.',\n properties: {\n type: {\n type: 'string',\n description: 'Type of the custom metadata field.',\n enum: [ 'Text',\n 'Textarea',\n 'Number',\n 'Date',\n 'Boolean',\n 'SingleSelect',\n 'MultiSelect'\n ]\n },\n defaultValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n },\n {\n type: 'array',\n title: 'Mixed',\n description: 'Default value should be of type array when custom metadata field type is set to `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n ],\n description: 'The default value for this custom metadata field. Date type of default value depends on the field type.\\n'\n },\n isValueRequired: {\n type: 'boolean',\n description: 'Specifies if the this custom metadata field is required or not.\\n'\n },\n maxLength: {\n type: 'number',\n description: 'Maximum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n maxValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Maximum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n minLength: {\n type: 'number',\n description: 'Minimum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n minValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Minimum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n selectOptions: {\n type: 'array',\n description: 'An array of allowed values when field type is `SingleSelect` or `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n },\n required: [ 'type'\n ]\n }\n },\n required: [ 'id',\n 'label',\n 'name',\n 'schema'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + includeDeleted: { + type: 'boolean', + description: 'Set it to `true` to include deleted field objects in the API response.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: [], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.customMetadataFields.list(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/custom-metadata-fields/update-custom-metadata-fields.ts b/packages/mcp-server/src/tools/custom-metadata-fields/update-custom-metadata-fields.ts new file mode 100644 index 00000000..7322540f --- /dev/null +++ b/packages/mcp-server/src/tools/custom-metadata-fields/update-custom-metadata-fields.ts @@ -0,0 +1,150 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'customMetadataFields', + operation: 'write', + tags: [], + httpMethod: 'patch', + httpPath: '/v1/customMetadataFields/{id}', + operationId: 'update-existing-field', +}; + +export const tool: Tool = { + name: 'update_custom_metadata_fields', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API updates the label or schema of an existing custom metadata field.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/custom_metadata_field',\n $defs: {\n custom_metadata_field: {\n type: 'object',\n description: 'Object containing details of a custom metadata field.',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the custom metadata field. Use this to update the field.'\n },\n label: {\n type: 'string',\n description: 'Human readable name of the custom metadata field. This name is displayed as form field label to the users while setting field value on the asset in the media library UI.\\n'\n },\n name: {\n type: 'string',\n description: 'API name of the custom metadata field. This becomes the key while setting `customMetadata` (key-value object) for an asset using upload or update API.\\n'\n },\n schema: {\n type: 'object',\n description: 'An object that describes the rules for the custom metadata field value.',\n properties: {\n type: {\n type: 'string',\n description: 'Type of the custom metadata field.',\n enum: [ 'Text',\n 'Textarea',\n 'Number',\n 'Date',\n 'Boolean',\n 'SingleSelect',\n 'MultiSelect'\n ]\n },\n defaultValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n },\n {\n type: 'array',\n title: 'Mixed',\n description: 'Default value should be of type array when custom metadata field type is set to `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n ],\n description: 'The default value for this custom metadata field. Date type of default value depends on the field type.\\n'\n },\n isValueRequired: {\n type: 'boolean',\n description: 'Specifies if the this custom metadata field is required or not.\\n'\n },\n maxLength: {\n type: 'number',\n description: 'Maximum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n maxValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Maximum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n minLength: {\n type: 'number',\n description: 'Minimum length of string. Only set if `type` is set to `Text` or `Textarea`.\\n'\n },\n minValue: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n }\n ],\n description: 'Minimum value of the field. Only set if field type is `Date` or `Number`. For `Date` type field, the value will be in ISO8601 string format. For `Number` type field, it will be a numeric value.\\n'\n },\n selectOptions: {\n type: 'array',\n description: 'An array of allowed values when field type is `SingleSelect` or `MultiSelect`.\\n',\n items: {\n anyOf: [ {\n type: 'string'\n },\n {\n type: 'number'\n },\n {\n type: 'boolean'\n }\n ]\n }\n }\n },\n required: [ 'type'\n ]\n }\n },\n required: [ 'id',\n 'label',\n 'name',\n 'schema'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + id: { + type: 'string', + }, + label: { + type: 'string', + description: + 'Human readable name of the custom metadata field. This should be unique across all non deleted custom metadata fields. This name is displayed as form field label to the users while setting field value on an asset in the media library UI. This parameter is required if `schema` is not provided.', + }, + schema: { + type: 'object', + description: + 'An object that describes the rules for the custom metadata key. This parameter is required if `label` is not provided. Note: `type` cannot be updated and will be ignored if sent with the `schema`. The schema will be validated as per the existing `type`.\n', + properties: { + defaultValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + { + type: 'array', + title: 'Mixed', + description: + 'Default value should be of type array when custom metadata field type is set to `MultiSelect`.\n', + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + ], + description: + 'The default value for this custom metadata field. This property is only required if `isValueRequired` property is set to `true`. The value should match the `type` of custom metadata field.\n', + }, + isValueRequired: { + type: 'boolean', + description: + 'Sets this custom metadata field as required. Setting custom metadata fields on an asset will throw error if the value for all required fields are not present in upload or update asset API request body.\n', + }, + maxLength: { + type: 'number', + description: + 'Maximum length of string. Only set this property if `type` is set to `Text` or `Textarea`.\n', + }, + maxValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + description: + 'Maximum value of the field. Only set this property if field type is `Date` or `Number`. For `Date` type field, set the minimum date in ISO8601 string format. For `Number` type field, set the minimum numeric value.\n', + }, + minLength: { + type: 'number', + description: + 'Minimum length of string. Only set this property if `type` is set to `Text` or `Textarea`.\n', + }, + minValue: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + description: + 'Minimum value of the field. Only set this property if field type is `Date` or `Number`. For `Date` type field, set the minimum date in ISO8601 string format. For `Number` type field, set the minimum numeric value.\n', + }, + selectOptions: { + type: 'array', + description: + 'An array of allowed values. This property is only required if `type` property is set to `SingleSelect` or `MultiSelect`.\n', + items: { + anyOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + { + type: 'boolean', + }, + ], + }, + }, + }, + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['id'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { id, jq_filter, ...body } = args as any; + return asTextContentResult( + await maybeFilter(jq_filter, await client.customMetadataFields.update(id, body)), + ); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/bulk/add-tags-files-bulk.ts b/packages/mcp-server/src/tools/files/bulk/add-tags-files-bulk.ts new file mode 100644 index 00000000..df867862 --- /dev/null +++ b/packages/mcp-server/src/tools/files/bulk/add-tags-files-bulk.ts @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.bulk', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/addTags', + operationId: 'add-tags-bulk', +}; + +export const tool: Tool = { + name: 'add_tags_files_bulk', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API adds tags to multiple files in bulk. A maximum of 50 files can be specified at a time.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n successfullyUpdatedFileIds: {\n type: 'array',\n description: 'An array of fileIds that in which tags were successfully added.\\n',\n items: {\n type: 'string'\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileIds: { + type: 'array', + description: 'An array of fileIds to which you want to add tags.\n', + items: { + type: 'string', + }, + }, + tags: { + type: 'array', + description: 'An array of tags that you want to add to the files.\n', + items: { + type: 'string', + }, + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileIds', 'tags'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.bulk.addTags(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/bulk/delete-files-bulk.ts b/packages/mcp-server/src/tools/files/bulk/delete-files-bulk.ts new file mode 100644 index 00000000..15f058f8 --- /dev/null +++ b/packages/mcp-server/src/tools/files/bulk/delete-files-bulk.ts @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.bulk', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/batch/deleteByFileIds', + operationId: 'delete-multiple-files', +}; + +export const tool: Tool = { + name: 'delete_files_bulk', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API deletes multiple files and all their file versions permanently.\n\nNote: If a file or specific transformation has been requested in the past, then the response is cached. Deleting a file does not purge the cache. You can purge the cache using purge cache API.\n\nA maximum of 100 files can be deleted at a time.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n successfullyDeletedFileIds: {\n type: 'array',\n description: 'An array of fileIds that were successfully deleted.\\n',\n items: {\n type: 'string'\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileIds: { + type: 'array', + description: 'An array of fileIds which you want to delete.\n', + items: { + type: 'string', + }, + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileIds'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.bulk.delete(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/bulk/remove-ai-tags-files-bulk.ts b/packages/mcp-server/src/tools/files/bulk/remove-ai-tags-files-bulk.ts new file mode 100644 index 00000000..3ab353ce --- /dev/null +++ b/packages/mcp-server/src/tools/files/bulk/remove-ai-tags-files-bulk.ts @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.bulk', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/removeAITags', + operationId: 'remove-ai-tags-bulk', +}; + +export const tool: Tool = { + name: 'remove_ai_tags_files_bulk', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API removes AITags from multiple files in bulk. A maximum of 50 files can be specified at a time.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n successfullyUpdatedFileIds: {\n type: 'array',\n description: 'An array of fileIds that in which AITags were successfully removed.\\n',\n items: {\n type: 'string'\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + AITags: { + type: 'array', + description: 'An array of AITags that you want to remove from the files.\n', + items: { + type: 'string', + }, + }, + fileIds: { + type: 'array', + description: 'An array of fileIds from which you want to remove AITags.\n', + items: { + type: 'string', + }, + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['AITags', 'fileIds'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.bulk.removeAITags(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/bulk/remove-tags-files-bulk.ts b/packages/mcp-server/src/tools/files/bulk/remove-tags-files-bulk.ts new file mode 100644 index 00000000..955898a6 --- /dev/null +++ b/packages/mcp-server/src/tools/files/bulk/remove-tags-files-bulk.ts @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.bulk', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/removeTags', + operationId: 'remove-tags-bulk', +}; + +export const tool: Tool = { + name: 'remove_tags_files_bulk', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API removes tags from multiple files in bulk. A maximum of 50 files can be specified at a time.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n successfullyUpdatedFileIds: {\n type: 'array',\n description: 'An array of fileIds that in which tags were successfully removed.\\n',\n items: {\n type: 'string'\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileIds: { + type: 'array', + description: 'An array of fileIds from which you want to remove tags.\n', + items: { + type: 'string', + }, + }, + tags: { + type: 'array', + description: 'An array of tags that you want to remove from the files.\n', + items: { + type: 'string', + }, + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileIds', 'tags'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.bulk.removeTags(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/copy-files.ts b/packages/mcp-server/src/tools/files/copy-files.ts new file mode 100644 index 00000000..94a5e514 --- /dev/null +++ b/packages/mcp-server/src/tools/files/copy-files.ts @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/copy', + operationId: 'copy-file', +}; + +export const tool: Tool = { + name: 'copy_files', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will copy a file from one folder to another. \n\nNote: If any file at the destination has the same name as the source file, then the source file and its versions (if `includeFileVersions` is set to true) will be appended to the destination file version history.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + destinationPath: { + type: 'string', + description: 'Full path to the folder you want to copy the above file into.\n', + }, + sourceFilePath: { + type: 'string', + description: 'The full path of the file you want to copy.\n', + }, + includeFileVersions: { + type: 'boolean', + description: + 'Option to copy all versions of a file. By default, only the current version of the file is copied. When set to true, all versions of the file will be copied. Default value - `false`.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['destinationPath', 'sourceFilePath'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.copy(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/delete-files.ts b/packages/mcp-server/src/tools/files/delete-files.ts new file mode 100644 index 00000000..61f46f2d --- /dev/null +++ b/packages/mcp-server/src/tools/files/delete-files.ts @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/files/{fileId}', + operationId: 'delete-file', +}; + +export const tool: Tool = { + name: 'delete_files', + description: + 'This API deletes the file and all its file versions permanently.\n\nNote: If a file or specific transformation has been requested in the past, then the response is cached. Deleting a file does not purge the cache. You can purge the cache using purge cache API.\n', + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + }, + required: ['fileId'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { fileId, ...body } = args as any; + const response = await client.files.delete(fileId).asResponse(); + return asTextContentResult(await response.text()); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/get-files.ts b/packages/mcp-server/src/tools/files/get-files.ts new file mode 100644 index 00000000..4f9b26dd --- /dev/null +++ b/packages/mcp-server/src/tools/files/get-files.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{fileId}/details', + operationId: 'get-file-details', +}; + +export const tool: Tool = { + name: 'get_files', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns an object with details or attributes about the current version of the file.\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/file',\n $defs: {\n file: {\n type: 'object',\n title: 'File & File Version',\n description: 'Object containing details of a file or file version.',\n properties: {\n AITags: {\n type: 'array',\n description: 'An array of tags assigned to the file by auto tagging.\\n',\n items: {\n type: 'object',\n properties: {\n confidence: {\n type: 'number',\n description: 'Confidence score of the tag.'\n },\n name: {\n type: 'string',\n description: 'Name of the tag.'\n },\n source: {\n type: 'string',\n description: 'Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`.'\n }\n }\n }\n },\n createdAt: {\n type: 'string',\n description: 'Date and time when the file was uploaded. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n customCoordinates: {\n type: 'string',\n description: 'An string with custom coordinates of the file.\\n'\n },\n customMetadata: {\n type: 'object',\n description: 'An object with custom metadata for the file.\\n',\n additionalProperties: true\n },\n description: {\n type: 'string',\n description: 'Optional text to describe the contents of the file. Can be set by the user or the ai-auto-description extension.\\n'\n },\n fileId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n filePath: {\n type: 'string',\n description: 'Path of the file. This is the path you would use in the URL to access the file. For example, if the file is at the root of the media library, the path will be `/file.jpg`. If the file is inside a folder named `images`, the path will be `/images/file.jpg`.\\n'\n },\n fileType: {\n type: 'string',\n description: 'Type of the file. Possible values are `image`, `non-image`.\\n'\n },\n hasAlpha: {\n type: 'boolean',\n description: 'Specifies if the image has an alpha channel.\\n'\n },\n height: {\n type: 'number',\n description: 'Height of the file.\\n'\n },\n isPrivateFile: {\n type: 'boolean',\n description: 'Specifies if the file is private or not.\\n'\n },\n isPublished: {\n type: 'boolean',\n description: 'Specifies if the file is published or not.\\n'\n },\n mime: {\n type: 'string',\n description: 'MIME type of the file.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n size: {\n type: 'number',\n description: 'Size of the file in bytes.\\n'\n },\n tags: {\n type: 'array',\n description: 'An array of tags assigned to the file. Tags are used to search files in the media library.\\n',\n items: {\n type: 'string'\n }\n },\n thumbnail: {\n type: 'string',\n description: 'URL of the thumbnail image. This URL is used to access the thumbnail image of the file in the media library.\\n'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'file',\n 'file-version'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the file was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n url: {\n type: 'string',\n description: 'URL of the file.\\n'\n },\n versionInfo: {\n type: 'object',\n description: 'An object with details of the file version.\\n',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the file version.'\n },\n name: {\n type: 'string',\n description: 'Name of the file version.'\n }\n }\n },\n width: {\n type: 'number',\n description: 'Width of the file.\\n'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { fileId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.get(fileId))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/metadata/get-files-metadata.ts b/packages/mcp-server/src/tools/files/metadata/get-files-metadata.ts new file mode 100644 index 00000000..39b1b033 --- /dev/null +++ b/packages/mcp-server/src/tools/files/metadata/get-files-metadata.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.metadata', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{fileId}/metadata', + operationId: 'get-uploaded-file-metadata', +}; + +export const tool: Tool = { + name: 'get_files_metadata', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nYou can programmatically get image EXIF, pHash, and other metadata for uploaded files in the ImageKit.io media library using this API.\n\nYou can also get the metadata in upload API response by passing `metadata` in `responseFields` parameter.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/metadata',\n $defs: {\n metadata: {\n type: 'object',\n description: 'JSON object containing metadata.',\n properties: {\n audioCodec: {\n type: 'string',\n description: 'The audio codec used in the video (only for video).'\n },\n bitRate: {\n type: 'integer',\n description: 'The bit rate of the video in kbps (only for video).'\n },\n density: {\n type: 'integer',\n description: 'The density of the image in DPI.'\n },\n duration: {\n type: 'integer',\n description: 'The duration of the video in seconds (only for video).'\n },\n exif: {\n type: 'object',\n properties: {\n exif: {\n type: 'object',\n description: 'Object containing Exif details.',\n properties: {\n ApertureValue: {\n type: 'number'\n },\n ColorSpace: {\n type: 'integer'\n },\n CreateDate: {\n type: 'string'\n },\n CustomRendered: {\n type: 'integer'\n },\n DateTimeOriginal: {\n type: 'string'\n },\n ExifImageHeight: {\n type: 'integer'\n },\n ExifImageWidth: {\n type: 'integer'\n },\n ExifVersion: {\n type: 'string'\n },\n ExposureCompensation: {\n type: 'number'\n },\n ExposureMode: {\n type: 'integer'\n },\n ExposureProgram: {\n type: 'integer'\n },\n ExposureTime: {\n type: 'number'\n },\n Flash: {\n type: 'integer'\n },\n FlashpixVersion: {\n type: 'string'\n },\n FNumber: {\n type: 'number'\n },\n FocalLength: {\n type: 'integer'\n },\n FocalPlaneResolutionUnit: {\n type: 'integer'\n },\n FocalPlaneXResolution: {\n type: 'number'\n },\n FocalPlaneYResolution: {\n type: 'number'\n },\n InteropOffset: {\n type: 'integer'\n },\n ISO: {\n type: 'integer'\n },\n MeteringMode: {\n type: 'integer'\n },\n SceneCaptureType: {\n type: 'integer'\n },\n ShutterSpeedValue: {\n type: 'number'\n },\n SubSecTime: {\n type: 'string'\n },\n WhiteBalance: {\n type: 'integer'\n }\n }\n },\n gps: {\n type: 'object',\n description: 'Object containing GPS information.',\n properties: {\n GPSVersionID: {\n type: 'array',\n items: {\n type: 'integer'\n }\n }\n }\n },\n image: {\n type: 'object',\n description: 'Object containing EXIF image information.',\n properties: {\n ExifOffset: {\n type: 'integer'\n },\n GPSInfo: {\n type: 'integer'\n },\n Make: {\n type: 'string'\n },\n Model: {\n type: 'string'\n },\n ModifyDate: {\n type: 'string'\n },\n Orientation: {\n type: 'integer'\n },\n ResolutionUnit: {\n type: 'integer'\n },\n Software: {\n type: 'string'\n },\n XResolution: {\n type: 'integer'\n },\n YCbCrPositioning: {\n type: 'integer'\n },\n YResolution: {\n type: 'integer'\n }\n }\n },\n interoperability: {\n type: 'object',\n description: 'JSON object.',\n properties: {\n InteropIndex: {\n type: 'string'\n },\n InteropVersion: {\n type: 'string'\n }\n }\n },\n makernote: {\n type: 'object',\n additionalProperties: true\n },\n thumbnail: {\n type: 'object',\n description: 'Object containing Thumbnail information.',\n properties: {\n Compression: {\n type: 'integer'\n },\n ResolutionUnit: {\n type: 'integer'\n },\n ThumbnailLength: {\n type: 'integer'\n },\n ThumbnailOffset: {\n type: 'integer'\n },\n XResolution: {\n type: 'integer'\n },\n YResolution: {\n type: 'integer'\n }\n }\n }\n }\n },\n format: {\n type: 'string',\n description: 'The format of the file (e.g., \\'jpg\\', \\'mp4\\').'\n },\n hasColorProfile: {\n type: 'boolean',\n description: 'Indicates if the image has a color profile.'\n },\n hasTransparency: {\n type: 'boolean',\n description: 'Indicates if the image contains transparent areas.'\n },\n height: {\n type: 'integer',\n description: 'The height of the image or video in pixels.'\n },\n pHash: {\n type: 'string',\n description: 'Perceptual hash of the image.'\n },\n quality: {\n type: 'integer',\n description: 'The quality indicator of the image.'\n },\n size: {\n type: 'integer',\n description: 'The file size in bytes.'\n },\n videoCodec: {\n type: 'string',\n description: 'The video codec used in the video (only for video).'\n },\n width: {\n type: 'integer',\n description: 'The width of the image or video in pixels.'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { fileId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.metadata.get(fileId))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/metadata/get-from-url-files-metadata.ts b/packages/mcp-server/src/tools/files/metadata/get-from-url-files-metadata.ts new file mode 100644 index 00000000..09386d6e --- /dev/null +++ b/packages/mcp-server/src/tools/files/metadata/get-from-url-files-metadata.ts @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.metadata', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/metadata', + operationId: 'get-metadata-from-url', +}; + +export const tool: Tool = { + name: 'get_from_url_files_metadata', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nGet image EXIF, pHash, and other metadata from ImageKit.io powered remote URL using this API.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/metadata',\n $defs: {\n metadata: {\n type: 'object',\n description: 'JSON object containing metadata.',\n properties: {\n audioCodec: {\n type: 'string',\n description: 'The audio codec used in the video (only for video).'\n },\n bitRate: {\n type: 'integer',\n description: 'The bit rate of the video in kbps (only for video).'\n },\n density: {\n type: 'integer',\n description: 'The density of the image in DPI.'\n },\n duration: {\n type: 'integer',\n description: 'The duration of the video in seconds (only for video).'\n },\n exif: {\n type: 'object',\n properties: {\n exif: {\n type: 'object',\n description: 'Object containing Exif details.',\n properties: {\n ApertureValue: {\n type: 'number'\n },\n ColorSpace: {\n type: 'integer'\n },\n CreateDate: {\n type: 'string'\n },\n CustomRendered: {\n type: 'integer'\n },\n DateTimeOriginal: {\n type: 'string'\n },\n ExifImageHeight: {\n type: 'integer'\n },\n ExifImageWidth: {\n type: 'integer'\n },\n ExifVersion: {\n type: 'string'\n },\n ExposureCompensation: {\n type: 'number'\n },\n ExposureMode: {\n type: 'integer'\n },\n ExposureProgram: {\n type: 'integer'\n },\n ExposureTime: {\n type: 'number'\n },\n Flash: {\n type: 'integer'\n },\n FlashpixVersion: {\n type: 'string'\n },\n FNumber: {\n type: 'number'\n },\n FocalLength: {\n type: 'integer'\n },\n FocalPlaneResolutionUnit: {\n type: 'integer'\n },\n FocalPlaneXResolution: {\n type: 'number'\n },\n FocalPlaneYResolution: {\n type: 'number'\n },\n InteropOffset: {\n type: 'integer'\n },\n ISO: {\n type: 'integer'\n },\n MeteringMode: {\n type: 'integer'\n },\n SceneCaptureType: {\n type: 'integer'\n },\n ShutterSpeedValue: {\n type: 'number'\n },\n SubSecTime: {\n type: 'string'\n },\n WhiteBalance: {\n type: 'integer'\n }\n }\n },\n gps: {\n type: 'object',\n description: 'Object containing GPS information.',\n properties: {\n GPSVersionID: {\n type: 'array',\n items: {\n type: 'integer'\n }\n }\n }\n },\n image: {\n type: 'object',\n description: 'Object containing EXIF image information.',\n properties: {\n ExifOffset: {\n type: 'integer'\n },\n GPSInfo: {\n type: 'integer'\n },\n Make: {\n type: 'string'\n },\n Model: {\n type: 'string'\n },\n ModifyDate: {\n type: 'string'\n },\n Orientation: {\n type: 'integer'\n },\n ResolutionUnit: {\n type: 'integer'\n },\n Software: {\n type: 'string'\n },\n XResolution: {\n type: 'integer'\n },\n YCbCrPositioning: {\n type: 'integer'\n },\n YResolution: {\n type: 'integer'\n }\n }\n },\n interoperability: {\n type: 'object',\n description: 'JSON object.',\n properties: {\n InteropIndex: {\n type: 'string'\n },\n InteropVersion: {\n type: 'string'\n }\n }\n },\n makernote: {\n type: 'object',\n additionalProperties: true\n },\n thumbnail: {\n type: 'object',\n description: 'Object containing Thumbnail information.',\n properties: {\n Compression: {\n type: 'integer'\n },\n ResolutionUnit: {\n type: 'integer'\n },\n ThumbnailLength: {\n type: 'integer'\n },\n ThumbnailOffset: {\n type: 'integer'\n },\n XResolution: {\n type: 'integer'\n },\n YResolution: {\n type: 'integer'\n }\n }\n }\n }\n },\n format: {\n type: 'string',\n description: 'The format of the file (e.g., \\'jpg\\', \\'mp4\\').'\n },\n hasColorProfile: {\n type: 'boolean',\n description: 'Indicates if the image has a color profile.'\n },\n hasTransparency: {\n type: 'boolean',\n description: 'Indicates if the image contains transparent areas.'\n },\n height: {\n type: 'integer',\n description: 'The height of the image or video in pixels.'\n },\n pHash: {\n type: 'string',\n description: 'Perceptual hash of the image.'\n },\n quality: {\n type: 'integer',\n description: 'The quality indicator of the image.'\n },\n size: {\n type: 'integer',\n description: 'The file size in bytes.'\n },\n videoCodec: {\n type: 'string',\n description: 'The video codec used in the video (only for video).'\n },\n width: {\n type: 'integer',\n description: 'The width of the image or video in pixels.'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Should be a valid file URL. It should be accessible using your ImageKit.io account.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['url'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.metadata.getFromURL(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/move-files.ts b/packages/mcp-server/src/tools/files/move-files.ts new file mode 100644 index 00000000..6a8e36de --- /dev/null +++ b/packages/mcp-server/src/tools/files/move-files.ts @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/files/move', + operationId: 'move-file', +}; + +export const tool: Tool = { + name: 'move_files', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will move a file and all its versions from one folder to another. \n\nNote: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + destinationPath: { + type: 'string', + description: 'Full path to the folder you want to move the above file into.\n', + }, + sourceFilePath: { + type: 'string', + description: 'The full path of the file you want to move.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['destinationPath', 'sourceFilePath'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.move(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/rename-files.ts b/packages/mcp-server/src/tools/files/rename-files.ts new file mode 100644 index 00000000..3bd46bb3 --- /dev/null +++ b/packages/mcp-server/src/tools/files/rename-files.ts @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'put', + httpPath: '/v1/files/rename', + operationId: 'rename-file', +}; + +export const tool: Tool = { + name: 'rename_files', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nYou can rename an already existing file in the media library using rename file API. This operation would rename all file versions of the file. \n\nNote: The old URLs will stop working. The file/file version URLs cached on CDN will continue to work unless a purge is requested.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n purgeRequestId: {\n type: 'string',\n description: 'Unique identifier of the purge request. This can be used to check the status of the purge request.\\n'\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + filePath: { + type: 'string', + description: 'The full path of the file you want to rename.\n', + }, + newFileName: { + type: 'string', + description: + 'The new name of the file. A filename can contain:\n\nAlphanumeric Characters: `a-z`, `A-Z`, `0-9` (including Unicode letters, marks, and numerals in other languages).\nSpecial Characters: `.`, `_`, and `-`.\n\nAny other character, including space, will be replaced by `_`.\n', + }, + purgeCache: { + type: 'boolean', + description: + "Option to purge cache for the old file and its versions' URLs.\n\nWhen set to true, it will internally issue a purge cache request on CDN to remove cached content of old file and its versions. This purge request is counted against your monthly purge quota.\n\nNote: If the old file were accessible at `https://ik.imagekit.io/demo/old-filename.jpg`, a purge cache request would be issued against `https://ik.imagekit.io/demo/old-filename.jpg*` (with a wildcard at the end). It will remove the file and its versions' URLs and any transformations made using query parameters on this file or its versions. However, the cache for file transformations made using path parameters will persist. You can purge them using the purge API. For more details, refer to the purge API documentation.\n\n\n\nDefault value - `false`\n", + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['filePath', 'newFileName'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.rename(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/update-files.ts b/packages/mcp-server/src/tools/files/update-files.ts new file mode 100644 index 00000000..a8113419 --- /dev/null +++ b/packages/mcp-server/src/tools/files/update-files.ts @@ -0,0 +1,196 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'patch', + httpPath: '/v1/files/{fileId}/details', + operationId: 'update-file-details', +}; + +export const tool: Tool = { + name: 'update_files', + description: + 'This API updates the details or attributes of the current version of the file. You can update `tags`, `customCoordinates`, `customMetadata`, publication status, remove existing `AITags` and apply extensions using this API.\n', + inputSchema: { + type: 'object', + anyOf: [ + { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + customCoordinates: { + type: 'string', + description: + 'Define an important area in the image in the format `x,y,width,height` e.g. `10,10,100,100`. Send `null` to unset this value.\n', + }, + customMetadata: { + type: 'object', + description: + 'A key-value data to be associated with the asset. To unset a key, send `null` value for that key. Before setting any custom metadata on an asset you have to create the field using custom metadata fields API.\n', + additionalProperties: true, + }, + description: { + type: 'string', + description: 'Optional text to describe the contents of the file.\n', + }, + extensions: { + $ref: '#/$defs/extensions', + }, + removeAITags: { + anyOf: [ + { + type: 'array', + items: { + type: 'string', + }, + }, + { + type: 'string', + enum: ['all'], + }, + ], + description: + 'An array of AITags associated with the file that you want to remove, e.g. `["car", "vehicle", "motorsports"]`. \n\nIf you want to remove all AITags associated with the file, send a string - "all".\n\nNote: The remove operation for `AITags` executes before any of the `extensions` are processed.\n', + }, + tags: { + type: 'array', + description: + 'An array of tags associated with the file, such as `["tag1", "tag2"]`. Send `null` to unset all tags associated with the file.\n', + items: { + type: 'string', + }, + }, + webhookUrl: { + type: 'string', + description: + 'The final status of extensions after they have completed execution will be delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) about the webhook payload structure.\n', + }, + }, + required: ['fileId'], + }, + { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + publish: { + type: 'object', + description: 'Configure the publication status of a file and its versions.\n', + properties: { + isPublished: { + type: 'boolean', + description: 'Set to `true` to publish the file. Set to `false` to unpublish the file.\n', + }, + includeFileVersions: { + type: 'boolean', + description: + 'Set to `true` to publish/unpublish all versions of the file. Set to `false` to publish/unpublish only the current version of the file.\n', + }, + }, + required: ['isPublished'], + }, + }, + required: ['fileId'], + }, + ], + $defs: { + extensions: { + type: 'array', + title: 'Extensions Array', + description: + 'Array of extensions to be applied to the asset. Each extension can be configured with specific parameters based on the extension type.\n', + items: { + anyOf: [ + { + type: 'object', + title: 'Remove background', + properties: { + name: { + type: 'string', + description: 'Specifies the background removal extension.', + enum: ['remove-bg'], + }, + options: { + type: 'object', + properties: { + add_shadow: { + type: 'boolean', + description: + 'Whether to add an artificial shadow to the result. Default is false. Note: Adding shadows is currently only supported for car photos.\n', + }, + bg_color: { + type: 'string', + description: + 'Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or color name (e.g., "green"). If this parameter is set, `bg_image_url` must be empty.\n', + }, + bg_image_url: { + type: 'string', + description: + 'Sets a background image from a URL. If this parameter is set, `bg_color` must be empty.\n', + }, + semitransparency: { + type: 'boolean', + description: + 'Allows semi-transparent regions in the result. Default is true. Note: Semitransparency is currently only supported for car windows.\n', + }, + }, + }, + }, + required: ['name'], + }, + { + type: 'object', + title: 'Auto tagging', + properties: { + maxTags: { + type: 'integer', + description: 'Maximum number of tags to attach to the asset.', + }, + minConfidence: { + type: 'integer', + description: 'Minimum confidence level for tags to be considered valid.', + }, + name: { + type: 'string', + description: 'Specifies the auto-tagging extension used.', + enum: ['google-auto-tagging', 'aws-auto-tagging'], + }, + }, + required: ['maxTags', 'minConfidence', 'name'], + }, + { + type: 'object', + title: 'Auto description', + properties: { + name: { + type: 'string', + description: 'Specifies the auto description extension.', + enum: ['ai-auto-description'], + }, + }, + required: ['name'], + }, + ], + }, + }, + }, + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { fileId, ...body } = args as any; + return asTextContentResult(await client.files.update(fileId, body)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/upload-files.ts b/packages/mcp-server/src/tools/files/upload-files.ts new file mode 100644 index 00000000..9173a4d8 --- /dev/null +++ b/packages/mcp-server/src/tools/files/upload-files.ts @@ -0,0 +1,331 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/api/v1/files/upload', + operationId: 'upload-file', +}; + +export const tool: Tool = { + name: 'upload_files', + description: + 'ImageKit.io allows you to upload files directly from both the server and client sides. For server-side uploads, private API key authentication is used. For client-side uploads, generate a one-time `token`, `signature`, and `expire` from your secure backend using private API. [Learn more](/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload) about how to implement client-side file upload.\n\nThe [V2 API](/docs/api-reference/upload-file/upload-file-v2) enhances security by verifying the entire payload using JWT.\n\n**File size limit** \\\nOn the free plan, the maximum upload file sizes are 20MB for images, audio, and raw files and 100MB for videos. On the paid plan, these limits increase to 40MB for images, audio, and raw files and 2GB for videos. These limits can be further increased with higher-tier plans.\n\n**Version limit** \\\nA file can have a maximum of 100 versions.\n\n**Demo applications**\n\n- A full-fledged [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), supporting file selections from local storage, URL, Dropbox, Google Drive, Instagram, and more.\n- [Quick start guides](/docs/quick-start-guides) for various frameworks and technologies.\n', + inputSchema: { + type: 'object', + properties: { + file: { + type: 'string', + description: + 'The API accepts any of the following:\n\n- **Binary data** – send the raw bytes as `multipart/form-data`.\n- **HTTP / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can fetch.\n- **Base64 string** – the file encoded as a Base64 data URI or plain Base64.\n\nWhen supplying a URL, the server must receive the response headers within 8 seconds; otherwise the request fails with 400 Bad Request.\n', + }, + fileName: { + type: 'string', + description: + 'The name with which the file has to be uploaded.\nThe file name can contain:\n\n - Alphanumeric Characters: `a-z`, `A-Z`, `0-9`.\n - Special Characters: `.`, `-`\n\nAny other character including space will be replaced by `_`\n', + }, + token: { + type: 'string', + description: + 'A unique value that the ImageKit.io server will use to recognize and prevent subsequent retries for the same request. We suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions. This field is only required for authentication when uploading a file from the client side.\n\n**Note**: Sending a value that has been used in the past will result in a validation error. Even if your previous request resulted in an error, you should always send a new value for this field.\n', + }, + checks: { + type: 'string', + description: + 'Server-side checks to run on the asset.\nRead more about [Upload API checks](/docs/api-reference/upload-file/upload-file#upload-api-checks).\n', + }, + customCoordinates: { + type: 'string', + description: + 'Define an important area in the image. This is only relevant for image type files.\n\n - To be passed as a string with the x and y coordinates of the top-left corner, and width and height of the area of interest in the format `x,y,width,height`. For example - `10,10,100,100`\n - Can be used with fo-customtransformation.\n - If this field is not specified and the file is overwritten, then customCoordinates will be removed.\n', + }, + customMetadata: { + type: 'object', + description: + 'JSON key-value pairs to associate with the asset. Create the custom metadata fields before setting these values.\n', + additionalProperties: true, + }, + description: { + type: 'string', + description: 'Optional text to describe the contents of the file.\n', + }, + expire: { + type: 'integer', + description: + 'The time until your signature is valid. It must be a [Unix time](https://en.wikipedia.org/wiki/Unix_time) in less than 1 hour into the future. It should be in seconds. This field is only required for authentication when uploading a file from the client side.\n', + }, + extensions: { + $ref: '#/$defs/extensions', + }, + folder: { + type: 'string', + description: + "The folder path in which the image has to be uploaded. If the folder(s) didn't exist before, a new folder(s) is created.\n\nThe folder name can contain:\n\n - Alphanumeric Characters: `a-z` , `A-Z` , `0-9`\n - Special Characters: `/` , `_` , `-`\n\nUsing multiple `/` creates a nested folder.\n", + }, + isPrivateFile: { + type: 'boolean', + description: + 'Whether to mark the file as private or not.\n\nIf `true`, the file is marked as private and is accessible only using named transformation or signed URL.\n', + }, + isPublished: { + type: 'boolean', + description: + 'Whether to upload file as published or not.\n\nIf `false`, the file is marked as unpublished, which restricts access to the file only via the media library. Files in draft or unpublished state can only be publicly accessed after being published.\n\nThe option to upload in draft state is only available in custom enterprise pricing plans.\n', + }, + overwriteAITags: { + type: 'boolean', + description: + 'If set to `true` and a file already exists at the exact location, its AITags will be removed. Set `overwriteAITags` to `false` to preserve AITags.\n', + }, + overwriteCustomMetadata: { + type: 'boolean', + description: + 'If the request does not have `customMetadata`, and a file already exists at the exact location, existing customMetadata will be removed.\n', + }, + overwriteFile: { + type: 'boolean', + description: + 'If `false` and `useUniqueFileName` is also `false`, and a file already exists at the exact location, upload API will return an error immediately.\n', + }, + overwriteTags: { + type: 'boolean', + description: + 'If the request does not have `tags`, and a file already exists at the exact location, existing tags will be removed.\n', + }, + publicKey: { + type: 'string', + description: + 'Your ImageKit.io public key. This field is only required for authentication when uploading a file from the client side.\n', + }, + responseFields: { + type: 'array', + description: 'Array of response field keys to include in the API response body.\n', + items: { + type: 'string', + enum: [ + 'tags', + 'customCoordinates', + 'isPrivateFile', + 'embeddedMetadata', + 'isPublished', + 'customMetadata', + 'metadata', + ], + }, + }, + signature: { + type: 'string', + description: + 'HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. Learn how to create a signature on the page below. This should be in lowercase.\n\nSignature must be calculated on the server-side. This field is only required for authentication when uploading a file from the client side.\n', + }, + tags: { + type: 'array', + description: + 'Set the tags while uploading the file.\nProvide an array of tag strings (e.g. `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must not exceed 500, and the `%` character is not allowed.\nIf this field is not specified and the file is overwritten, the existing tags will be removed.\n', + items: { + type: 'string', + }, + }, + transformation: { + type: 'object', + description: + "Configure pre-processing (`pre`) and post-processing (`post`) transformations.\n\n- `pre` — applied before the file is uploaded to the Media Library. \n Useful for reducing file size or applying basic optimizations upfront (e.g., resize, compress).\n\n- `post` — applied immediately after upload. \n Ideal for generating transformed versions (like video encodes or thumbnails) in advance, so they're ready for delivery without delay.\n\nYou can mix and match any combination of post-processing types.\n", + properties: { + post: { + type: 'array', + description: + 'List of transformations to apply *after* the file is uploaded. \nEach item must match one of the following types:\n`transformation`, `gif-to-video`, `thumbnail`, `abs`.\n', + items: { + anyOf: [ + { + type: 'object', + title: 'Simple post-transformation', + properties: { + type: { + type: 'string', + description: 'Transformation type.', + enum: ['transformation'], + }, + value: { + type: 'string', + description: + 'Transformation string (e.g. `w-200,h-200`). \nSame syntax as ImageKit URL-based transformations.\n', + }, + }, + required: ['type', 'value'], + }, + { + type: 'object', + title: 'Convert GIF to video', + properties: { + type: { + type: 'string', + description: 'Converts an animated GIF into an MP4.', + enum: ['gif-to-video'], + }, + value: { + type: 'string', + description: + 'Optional transformation string to apply to the output video. \n**Example**: `q-80`\n', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Generate a thumbnail', + properties: { + type: { + type: 'string', + description: 'Generates a thumbnail image.', + enum: ['thumbnail'], + }, + value: { + type: 'string', + description: 'Optional transformation string. \n**Example**: `w-150,h-150`\n', + }, + }, + required: ['type'], + }, + { + type: 'object', + title: 'Adaptive Bitrate Streaming', + properties: { + protocol: { + type: 'string', + description: 'Streaming protocol to use (`hls` or `dash`).', + enum: ['hls', 'dash'], + }, + type: { + type: 'string', + description: 'Adaptive Bitrate Streaming (ABS) setup.', + enum: ['abs'], + }, + value: { + type: 'string', + description: + 'List of different representations you want to create separated by an underscore.\n', + }, + }, + required: ['protocol', 'type', 'value'], + }, + ], + }, + }, + pre: { + type: 'string', + description: + 'Transformation string to apply before uploading the file to the Media Library. Useful for optimizing files at ingestion.\n', + }, + }, + }, + useUniqueFileName: { + type: 'boolean', + description: + 'Whether to use a unique filename for this file or not.\n\nIf `true`, ImageKit.io will add a unique suffix to the filename parameter to get a unique filename.\n\nIf `false`, then the image is uploaded with the provided filename parameter, and any existing file with the same name is replaced.\n', + }, + webhookUrl: { + type: 'string', + description: + 'The final status of extensions after they have completed execution will be delivered to this endpoint as a POST request. [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) about the webhook payload structure.\n', + }, + }, + required: ['file', 'fileName'], + $defs: { + extensions: { + type: 'array', + title: 'Extensions Array', + description: + 'Array of extensions to be applied to the asset. Each extension can be configured with specific parameters based on the extension type.\n', + items: { + anyOf: [ + { + type: 'object', + title: 'Remove background', + properties: { + name: { + type: 'string', + description: 'Specifies the background removal extension.', + enum: ['remove-bg'], + }, + options: { + type: 'object', + properties: { + add_shadow: { + type: 'boolean', + description: + 'Whether to add an artificial shadow to the result. Default is false. Note: Adding shadows is currently only supported for car photos.\n', + }, + bg_color: { + type: 'string', + description: + 'Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or color name (e.g., "green"). If this parameter is set, `bg_image_url` must be empty.\n', + }, + bg_image_url: { + type: 'string', + description: + 'Sets a background image from a URL. If this parameter is set, `bg_color` must be empty.\n', + }, + semitransparency: { + type: 'boolean', + description: + 'Allows semi-transparent regions in the result. Default is true. Note: Semitransparency is currently only supported for car windows.\n', + }, + }, + }, + }, + required: ['name'], + }, + { + type: 'object', + title: 'Auto tagging', + properties: { + maxTags: { + type: 'integer', + description: 'Maximum number of tags to attach to the asset.', + }, + minConfidence: { + type: 'integer', + description: 'Minimum confidence level for tags to be considered valid.', + }, + name: { + type: 'string', + description: 'Specifies the auto-tagging extension used.', + enum: ['google-auto-tagging', 'aws-auto-tagging'], + }, + }, + required: ['maxTags', 'minConfidence', 'name'], + }, + { + type: 'object', + title: 'Auto description', + properties: { + name: { + type: 'string', + description: 'Specifies the auto description extension.', + enum: ['ai-auto-description'], + }, + }, + required: ['name'], + }, + ], + }, + }, + }, + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const body = args as any; + return asTextContentResult(await client.files.upload(body)); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/versions/delete-files-versions.ts b/packages/mcp-server/src/tools/files/versions/delete-files-versions.ts new file mode 100644 index 00000000..f8a75c37 --- /dev/null +++ b/packages/mcp-server/src/tools/files/versions/delete-files-versions.ts @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.versions', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/files/{fileId}/versions/{versionId}', + operationId: 'delete-file-version', +}; + +export const tool: Tool = { + name: 'delete_files_versions', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API deletes a non-current file version permanently. The API returns an empty response.\n\nNote: If you want to delete all versions of a file, use the delete file API.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + versionId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId', 'versionId'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { versionId, jq_filter, ...body } = args as any; + return asTextContentResult( + await maybeFilter(jq_filter, await client.files.versions.delete(versionId, body)), + ); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/versions/get-files-versions.ts b/packages/mcp-server/src/tools/files/versions/get-files-versions.ts new file mode 100644 index 00000000..22b68d8e --- /dev/null +++ b/packages/mcp-server/src/tools/files/versions/get-files-versions.ts @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.versions', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{fileId}/versions/{versionId}', + operationId: 'get-file-version-details', +}; + +export const tool: Tool = { + name: 'get_files_versions', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns an object with details or attributes of a file version.\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/file',\n $defs: {\n file: {\n type: 'object',\n title: 'File & File Version',\n description: 'Object containing details of a file or file version.',\n properties: {\n AITags: {\n type: 'array',\n description: 'An array of tags assigned to the file by auto tagging.\\n',\n items: {\n type: 'object',\n properties: {\n confidence: {\n type: 'number',\n description: 'Confidence score of the tag.'\n },\n name: {\n type: 'string',\n description: 'Name of the tag.'\n },\n source: {\n type: 'string',\n description: 'Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`.'\n }\n }\n }\n },\n createdAt: {\n type: 'string',\n description: 'Date and time when the file was uploaded. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n customCoordinates: {\n type: 'string',\n description: 'An string with custom coordinates of the file.\\n'\n },\n customMetadata: {\n type: 'object',\n description: 'An object with custom metadata for the file.\\n',\n additionalProperties: true\n },\n description: {\n type: 'string',\n description: 'Optional text to describe the contents of the file. Can be set by the user or the ai-auto-description extension.\\n'\n },\n fileId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n filePath: {\n type: 'string',\n description: 'Path of the file. This is the path you would use in the URL to access the file. For example, if the file is at the root of the media library, the path will be `/file.jpg`. If the file is inside a folder named `images`, the path will be `/images/file.jpg`.\\n'\n },\n fileType: {\n type: 'string',\n description: 'Type of the file. Possible values are `image`, `non-image`.\\n'\n },\n hasAlpha: {\n type: 'boolean',\n description: 'Specifies if the image has an alpha channel.\\n'\n },\n height: {\n type: 'number',\n description: 'Height of the file.\\n'\n },\n isPrivateFile: {\n type: 'boolean',\n description: 'Specifies if the file is private or not.\\n'\n },\n isPublished: {\n type: 'boolean',\n description: 'Specifies if the file is published or not.\\n'\n },\n mime: {\n type: 'string',\n description: 'MIME type of the file.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n size: {\n type: 'number',\n description: 'Size of the file in bytes.\\n'\n },\n tags: {\n type: 'array',\n description: 'An array of tags assigned to the file. Tags are used to search files in the media library.\\n',\n items: {\n type: 'string'\n }\n },\n thumbnail: {\n type: 'string',\n description: 'URL of the thumbnail image. This URL is used to access the thumbnail image of the file in the media library.\\n'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'file',\n 'file-version'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the file was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n url: {\n type: 'string',\n description: 'URL of the file.\\n'\n },\n versionInfo: {\n type: 'object',\n description: 'An object with details of the file version.\\n',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the file version.'\n },\n name: {\n type: 'string',\n description: 'Name of the file version.'\n }\n }\n },\n width: {\n type: 'number',\n description: 'Width of the file.\\n'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + versionId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId', 'versionId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { versionId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.versions.get(versionId, body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/versions/list-files-versions.ts b/packages/mcp-server/src/tools/files/versions/list-files-versions.ts new file mode 100644 index 00000000..aa6d5d2b --- /dev/null +++ b/packages/mcp-server/src/tools/files/versions/list-files-versions.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.versions', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/files/{fileId}/versions', + operationId: 'list-file-versions', +}; + +export const tool: Tool = { + name: 'list_files_versions', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns details of all versions of a file.\n\n\n# Response Schema\n```json\n{\n type: 'array',\n items: {\n $ref: '#/$defs/file'\n },\n $defs: {\n file: {\n type: 'object',\n title: 'File & File Version',\n description: 'Object containing details of a file or file version.',\n properties: {\n AITags: {\n type: 'array',\n description: 'An array of tags assigned to the file by auto tagging.\\n',\n items: {\n type: 'object',\n properties: {\n confidence: {\n type: 'number',\n description: 'Confidence score of the tag.'\n },\n name: {\n type: 'string',\n description: 'Name of the tag.'\n },\n source: {\n type: 'string',\n description: 'Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`.'\n }\n }\n }\n },\n createdAt: {\n type: 'string',\n description: 'Date and time when the file was uploaded. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n customCoordinates: {\n type: 'string',\n description: 'An string with custom coordinates of the file.\\n'\n },\n customMetadata: {\n type: 'object',\n description: 'An object with custom metadata for the file.\\n',\n additionalProperties: true\n },\n description: {\n type: 'string',\n description: 'Optional text to describe the contents of the file. Can be set by the user or the ai-auto-description extension.\\n'\n },\n fileId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n filePath: {\n type: 'string',\n description: 'Path of the file. This is the path you would use in the URL to access the file. For example, if the file is at the root of the media library, the path will be `/file.jpg`. If the file is inside a folder named `images`, the path will be `/images/file.jpg`.\\n'\n },\n fileType: {\n type: 'string',\n description: 'Type of the file. Possible values are `image`, `non-image`.\\n'\n },\n hasAlpha: {\n type: 'boolean',\n description: 'Specifies if the image has an alpha channel.\\n'\n },\n height: {\n type: 'number',\n description: 'Height of the file.\\n'\n },\n isPrivateFile: {\n type: 'boolean',\n description: 'Specifies if the file is private or not.\\n'\n },\n isPublished: {\n type: 'boolean',\n description: 'Specifies if the file is published or not.\\n'\n },\n mime: {\n type: 'string',\n description: 'MIME type of the file.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n size: {\n type: 'number',\n description: 'Size of the file in bytes.\\n'\n },\n tags: {\n type: 'array',\n description: 'An array of tags assigned to the file. Tags are used to search files in the media library.\\n',\n items: {\n type: 'string'\n }\n },\n thumbnail: {\n type: 'string',\n description: 'URL of the thumbnail image. This URL is used to access the thumbnail image of the file in the media library.\\n'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'file',\n 'file-version'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the file was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n url: {\n type: 'string',\n description: 'URL of the file.\\n'\n },\n versionInfo: {\n type: 'object',\n description: 'An object with details of the file version.\\n',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the file version.'\n },\n name: {\n type: 'string',\n description: 'Name of the file version.'\n }\n }\n },\n width: {\n type: 'number',\n description: 'Width of the file.\\n'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { fileId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.files.versions.list(fileId))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/files/versions/restore-files-versions.ts b/packages/mcp-server/src/tools/files/versions/restore-files-versions.ts new file mode 100644 index 00000000..4e9e9197 --- /dev/null +++ b/packages/mcp-server/src/tools/files/versions/restore-files-versions.ts @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'files.versions', + operation: 'write', + tags: [], + httpMethod: 'put', + httpPath: '/v1/files/{fileId}/versions/{versionId}/restore', + operationId: 'restore-file-version', +}; + +export const tool: Tool = { + name: 'restore_files_versions', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API restores a file version as the current file version.\n\n\n# Response Schema\n```json\n{\n $ref: '#/$defs/file',\n $defs: {\n file: {\n type: 'object',\n title: 'File & File Version',\n description: 'Object containing details of a file or file version.',\n properties: {\n AITags: {\n type: 'array',\n description: 'An array of tags assigned to the file by auto tagging.\\n',\n items: {\n type: 'object',\n properties: {\n confidence: {\n type: 'number',\n description: 'Confidence score of the tag.'\n },\n name: {\n type: 'string',\n description: 'Name of the tag.'\n },\n source: {\n type: 'string',\n description: 'Source of the tag. Possible values are `google-auto-tagging` and `aws-auto-tagging`.'\n }\n }\n }\n },\n createdAt: {\n type: 'string',\n description: 'Date and time when the file was uploaded. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n customCoordinates: {\n type: 'string',\n description: 'An string with custom coordinates of the file.\\n'\n },\n customMetadata: {\n type: 'object',\n description: 'An object with custom metadata for the file.\\n',\n additionalProperties: true\n },\n description: {\n type: 'string',\n description: 'Optional text to describe the contents of the file. Can be set by the user or the ai-auto-description extension.\\n'\n },\n fileId: {\n type: 'string',\n description: 'Unique identifier of the asset.'\n },\n filePath: {\n type: 'string',\n description: 'Path of the file. This is the path you would use in the URL to access the file. For example, if the file is at the root of the media library, the path will be `/file.jpg`. If the file is inside a folder named `images`, the path will be `/images/file.jpg`.\\n'\n },\n fileType: {\n type: 'string',\n description: 'Type of the file. Possible values are `image`, `non-image`.\\n'\n },\n hasAlpha: {\n type: 'boolean',\n description: 'Specifies if the image has an alpha channel.\\n'\n },\n height: {\n type: 'number',\n description: 'Height of the file.\\n'\n },\n isPrivateFile: {\n type: 'boolean',\n description: 'Specifies if the file is private or not.\\n'\n },\n isPublished: {\n type: 'boolean',\n description: 'Specifies if the file is published or not.\\n'\n },\n mime: {\n type: 'string',\n description: 'MIME type of the file.\\n'\n },\n name: {\n type: 'string',\n description: 'Name of the asset.'\n },\n size: {\n type: 'number',\n description: 'Size of the file in bytes.\\n'\n },\n tags: {\n type: 'array',\n description: 'An array of tags assigned to the file. Tags are used to search files in the media library.\\n',\n items: {\n type: 'string'\n }\n },\n thumbnail: {\n type: 'string',\n description: 'URL of the thumbnail image. This URL is used to access the thumbnail image of the file in the media library.\\n'\n },\n type: {\n type: 'string',\n description: 'Type of the asset.',\n enum: [ 'file',\n 'file-version'\n ]\n },\n updatedAt: {\n type: 'string',\n description: 'Date and time when the file was last updated. The date and time is in ISO8601 format.\\n',\n format: 'date-time'\n },\n url: {\n type: 'string',\n description: 'URL of the file.\\n'\n },\n versionInfo: {\n type: 'object',\n description: 'An object with details of the file version.\\n',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the file version.'\n },\n name: {\n type: 'string',\n description: 'Name of the file version.'\n }\n }\n },\n width: {\n type: 'number',\n description: 'Width of the file.\\n'\n }\n }\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + fileId: { + type: 'string', + }, + versionId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['fileId', 'versionId'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { versionId, jq_filter, ...body } = args as any; + return asTextContentResult( + await maybeFilter(jq_filter, await client.files.versions.restore(versionId, body)), + ); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/copy-folders.ts b/packages/mcp-server/src/tools/folders/copy-folders.ts new file mode 100644 index 00000000..17dd44b8 --- /dev/null +++ b/packages/mcp-server/src/tools/folders/copy-folders.ts @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/bulkJobs/copyFolder', + operationId: 'copy-folder', +}; + +export const tool: Tool = { + name: 'copy_folders', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will copy one folder into another. The selected folder, its nested folders, files, and their versions (in `includeVersions` is set to true) are copied in this operation. Note: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file version history.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n title: 'Async Bulk Job Response',\n description: 'Job submitted successfully. A `jobId` will be returned.',\n properties: {\n jobId: {\n type: 'string',\n description: 'Unique identifier of the bulk job. This can be used to check the status of the bulk job.\\n'\n }\n },\n required: [ 'jobId'\n ]\n}\n```", + inputSchema: { + type: 'object', + properties: { + destinationPath: { + type: 'string', + description: 'Full path to the destination folder where you want to copy the source folder into.\n', + }, + sourceFolderPath: { + type: 'string', + description: 'The full path to the source folder you want to copy.\n', + }, + includeVersions: { + type: 'boolean', + description: + 'Option to copy all versions of files that are nested inside the selected folder. By default, only the current version of each file will be copied. When set to true, all versions of each file will be copied. Default value - `false`.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['destinationPath', 'sourceFolderPath'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.copy(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/create-folders.ts b/packages/mcp-server/src/tools/folders/create-folders.ts new file mode 100644 index 00000000..00bbe6fb --- /dev/null +++ b/packages/mcp-server/src/tools/folders/create-folders.ts @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/folder', + operationId: 'create-folder', +}; + +export const tool: Tool = { + name: 'create_folders', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will create a new folder. You can specify the folder name and location of the parent folder where this new folder should be created.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + folderName: { + type: 'string', + description: + 'The folder will be created with this name. \n\nAll characters except alphabets and numbers (inclusive of unicode letters, marks, and numerals in other languages) will be replaced by an underscore i.e. `_`.\n', + }, + parentFolderPath: { + type: 'string', + description: + "The folder where the new folder should be created, for root use `/` else the path e.g. `containing/folder/`.\n\nNote: If any folder(s) is not present in the parentFolderPath parameter, it will be automatically created. For example, if you pass `/product/images/summer`, then `product`, `images`, and `summer` folders will be created if they don't already exist.\n", + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['folderName', 'parentFolderPath'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.create(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/delete-folders.ts b/packages/mcp-server/src/tools/folders/delete-folders.ts new file mode 100644 index 00000000..65a2bb1a --- /dev/null +++ b/packages/mcp-server/src/tools/folders/delete-folders.ts @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders', + operation: 'write', + tags: [], + httpMethod: 'delete', + httpPath: '/v1/folder', + operationId: 'delete-folder', +}; + +export const tool: Tool = { + name: 'delete_folders', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will delete a folder and all its contents permanently. The API returns an empty response.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {}\n}\n```", + inputSchema: { + type: 'object', + properties: { + folderPath: { + type: 'string', + description: 'Full path to the folder you want to delete. For example `/folder/to/delete/`.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['folderPath'], + }, + annotations: { + idempotentHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.delete(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/job/get-folders-job.ts b/packages/mcp-server/src/tools/folders/job/get-folders-job.ts new file mode 100644 index 00000000..982e5a24 --- /dev/null +++ b/packages/mcp-server/src/tools/folders/job/get-folders-job.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders.job', + operation: 'read', + tags: [], + httpMethod: 'get', + httpPath: '/v1/bulkJobs/{jobId}', + operationId: 'bulk-job-status', +}; + +export const tool: Tool = { + name: 'get_folders_job', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API returns the status of a bulk job like copy and move folder operations.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n properties: {\n jobId: {\n type: 'string',\n description: 'Unique identifier of the bulk job.\\n'\n },\n purgeRequestId: {\n type: 'string',\n description: 'Unique identifier of the purge request. This will be present only if `purgeCache` is set to `true` in the rename folder API request.\\n'\n },\n status: {\n type: 'string',\n description: 'Status of the bulk job.',\n enum: [ 'Pending',\n 'Completed'\n ]\n },\n type: {\n type: 'string',\n description: 'Type of the bulk job.',\n enum: [ 'COPY_FOLDER',\n 'MOVE_FOLDER',\n 'RENAME_FOLDER'\n ]\n }\n }\n}\n```", + inputSchema: { + type: 'object', + properties: { + jobId: { + type: 'string', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['jobId'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jobId, jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.job.get(jobId))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/move-folders.ts b/packages/mcp-server/src/tools/folders/move-folders.ts new file mode 100644 index 00000000..503199ba --- /dev/null +++ b/packages/mcp-server/src/tools/folders/move-folders.ts @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/bulkJobs/moveFolder', + operationId: 'move-folder', +}; + +export const tool: Tool = { + name: 'move_folders', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis will move one folder into another. The selected folder, its nested folders, files, and their versions are moved in this operation. Note: If any file at the destination has the same name as the source file, then the source file and its versions will be appended to the destination file version history.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n title: 'Async Bulk Job Response',\n description: 'Job submitted successfully. A `jobId` will be returned.',\n properties: {\n jobId: {\n type: 'string',\n description: 'Unique identifier of the bulk job. This can be used to check the status of the bulk job.\\n'\n }\n },\n required: [ 'jobId'\n ]\n}\n```", + inputSchema: { + type: 'object', + properties: { + destinationPath: { + type: 'string', + description: 'Full path to the destination folder where you want to move the source folder into.\n', + }, + sourceFolderPath: { + type: 'string', + description: 'The full path to the source folder you want to move.\n', + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['destinationPath', 'sourceFolderPath'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.move(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/folders/rename-folders.ts b/packages/mcp-server/src/tools/folders/rename-folders.ts new file mode 100644 index 00000000..a77ca1b2 --- /dev/null +++ b/packages/mcp-server/src/tools/folders/rename-folders.ts @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { maybeFilter } from 'imagekit-api-mcp/filtering'; +import { Metadata, asTextContentResult } from 'imagekit-api-mcp/tools/types'; + +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import ImageKit from '@imagekit/nodejs'; + +export const metadata: Metadata = { + resource: 'folders', + operation: 'write', + tags: [], + httpMethod: 'post', + httpPath: '/v1/bulkJobs/renameFolder', + operationId: 'rename-folder', +}; + +export const tool: Tool = { + name: 'rename_folders', + description: + "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nThis API allows you to rename an existing folder. The folder and all its nested assets and sub-folders will remain unchanged, but their paths will be updated to reflect the new folder name.\n\n\n# Response Schema\n```json\n{\n type: 'object',\n title: 'Async Bulk Job Response',\n description: 'Job submitted successfully. A `jobId` will be returned.',\n properties: {\n jobId: {\n type: 'string',\n description: 'Unique identifier of the bulk job. This can be used to check the status of the bulk job.\\n'\n }\n },\n required: [ 'jobId'\n ]\n}\n```", + inputSchema: { + type: 'object', + properties: { + folderPath: { + type: 'string', + description: 'The full path to the folder you want to rename.\n', + }, + newFolderName: { + type: 'string', + description: + 'The new name for the folder.\n\nAll characters except alphabets and numbers (inclusive of unicode letters, marks, and numerals in other languages) and `-` will be replaced by an underscore i.e. `_`.\n', + }, + purgeCache: { + type: 'boolean', + description: + "Option to purge cache for the old nested files and their versions' URLs.\n\nWhen set to true, it will internally issue a purge cache request on CDN to remove the cached content of the old nested files and their versions. There will only be one purge request for all the nested files, which will be counted against your monthly purge quota.\n\nNote: A purge cache request will be issued against `https://ik.imagekit.io/old/folder/path*` (with a wildcard at the end). This will remove all nested files, their versions' URLs, and any transformations made using query parameters on these files or their versions. However, the cache for file transformations made using path parameters will persist. You can purge them using the purge API. For more details, refer to the purge API documentation.\n\nDefault value - `false`\n", + }, + jq_filter: { + type: 'string', + title: 'jq Filter', + description: + 'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).', + }, + }, + required: ['folderPath', 'newFolderName'], + }, + annotations: {}, +}; + +export const handler = async (client: ImageKit, args: Record | undefined) => { + const { jq_filter, ...body } = args as any; + return asTextContentResult(await maybeFilter(jq_filter, await client.folders.rename(body))); +}; + +export default { metadata, tool, handler }; diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts new file mode 100644 index 00000000..ba7083d7 --- /dev/null +++ b/packages/mcp-server/src/tools/index.ts @@ -0,0 +1,153 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Metadata, Endpoint, HandlerFunction } from './types'; + +export { Metadata, Endpoint, HandlerFunction }; + +import create_custom_metadata_fields from './custom-metadata-fields/create-custom-metadata-fields'; +import update_custom_metadata_fields from './custom-metadata-fields/update-custom-metadata-fields'; +import list_custom_metadata_fields from './custom-metadata-fields/list-custom-metadata-fields'; +import delete_custom_metadata_fields from './custom-metadata-fields/delete-custom-metadata-fields'; +import update_files from './files/update-files'; +import delete_files from './files/delete-files'; +import copy_files from './files/copy-files'; +import get_files from './files/get-files'; +import move_files from './files/move-files'; +import rename_files from './files/rename-files'; +import upload_files from './files/upload-files'; +import delete_files_bulk from './files/bulk/delete-files-bulk'; +import add_tags_files_bulk from './files/bulk/add-tags-files-bulk'; +import remove_ai_tags_files_bulk from './files/bulk/remove-ai-tags-files-bulk'; +import remove_tags_files_bulk from './files/bulk/remove-tags-files-bulk'; +import list_files_versions from './files/versions/list-files-versions'; +import delete_files_versions from './files/versions/delete-files-versions'; +import get_files_versions from './files/versions/get-files-versions'; +import restore_files_versions from './files/versions/restore-files-versions'; +import get_files_metadata from './files/metadata/get-files-metadata'; +import get_from_url_files_metadata from './files/metadata/get-from-url-files-metadata'; +import list_assets from './assets/list-assets'; +import create_cache_invalidation from './cache/invalidation/create-cache-invalidation'; +import get_cache_invalidation from './cache/invalidation/get-cache-invalidation'; +import create_folders from './folders/create-folders'; +import delete_folders from './folders/delete-folders'; +import copy_folders from './folders/copy-folders'; +import move_folders from './folders/move-folders'; +import rename_folders from './folders/rename-folders'; +import get_folders_job from './folders/job/get-folders-job'; +import get_accounts_usage from './accounts/usage/get-accounts-usage'; +import create_accounts_origins from './accounts/origins/create-accounts-origins'; +import update_accounts_origins from './accounts/origins/update-accounts-origins'; +import list_accounts_origins from './accounts/origins/list-accounts-origins'; +import delete_accounts_origins from './accounts/origins/delete-accounts-origins'; +import get_accounts_origins from './accounts/origins/get-accounts-origins'; +import create_accounts_url_endpoints from './accounts/url-endpoints/create-accounts-url-endpoints'; +import update_accounts_url_endpoints from './accounts/url-endpoints/update-accounts-url-endpoints'; +import list_accounts_url_endpoints from './accounts/url-endpoints/list-accounts-url-endpoints'; +import delete_accounts_url_endpoints from './accounts/url-endpoints/delete-accounts-url-endpoints'; +import get_accounts_url_endpoints from './accounts/url-endpoints/get-accounts-url-endpoints'; +import upload_v2_beta_files from './beta/v2/files/upload-v2-beta-files'; + +export const endpoints: Endpoint[] = []; + +function addEndpoint(endpoint: Endpoint) { + endpoints.push(endpoint); +} + +addEndpoint(create_custom_metadata_fields); +addEndpoint(update_custom_metadata_fields); +addEndpoint(list_custom_metadata_fields); +addEndpoint(delete_custom_metadata_fields); +addEndpoint(update_files); +addEndpoint(delete_files); +addEndpoint(copy_files); +addEndpoint(get_files); +addEndpoint(move_files); +addEndpoint(rename_files); +addEndpoint(upload_files); +addEndpoint(delete_files_bulk); +addEndpoint(add_tags_files_bulk); +addEndpoint(remove_ai_tags_files_bulk); +addEndpoint(remove_tags_files_bulk); +addEndpoint(list_files_versions); +addEndpoint(delete_files_versions); +addEndpoint(get_files_versions); +addEndpoint(restore_files_versions); +addEndpoint(get_files_metadata); +addEndpoint(get_from_url_files_metadata); +addEndpoint(list_assets); +addEndpoint(create_cache_invalidation); +addEndpoint(get_cache_invalidation); +addEndpoint(create_folders); +addEndpoint(delete_folders); +addEndpoint(copy_folders); +addEndpoint(move_folders); +addEndpoint(rename_folders); +addEndpoint(get_folders_job); +addEndpoint(get_accounts_usage); +addEndpoint(create_accounts_origins); +addEndpoint(update_accounts_origins); +addEndpoint(list_accounts_origins); +addEndpoint(delete_accounts_origins); +addEndpoint(get_accounts_origins); +addEndpoint(create_accounts_url_endpoints); +addEndpoint(update_accounts_url_endpoints); +addEndpoint(list_accounts_url_endpoints); +addEndpoint(delete_accounts_url_endpoints); +addEndpoint(get_accounts_url_endpoints); +addEndpoint(upload_v2_beta_files); + +export type Filter = { + type: 'resource' | 'operation' | 'tag' | 'tool'; + op: 'include' | 'exclude'; + value: string; +}; + +export function query(filters: Filter[], endpoints: Endpoint[]): Endpoint[] { + const allExcludes = filters.length > 0 && filters.every((filter) => filter.op === 'exclude'); + const unmatchedFilters = new Set(filters); + + const filtered = endpoints.filter((endpoint: Endpoint) => { + let included = false || allExcludes; + + for (const filter of filters) { + if (match(filter, endpoint)) { + unmatchedFilters.delete(filter); + included = filter.op === 'include'; + } + } + + return included; + }); + + // Check if any filters didn't match + const unmatched = Array.from(unmatchedFilters).filter((f) => f.type === 'tool' || f.type === 'resource'); + if (unmatched.length > 0) { + throw new Error( + `The following filters did not match any endpoints: ${unmatched + .map((f) => `${f.type}=${f.value}`) + .join(', ')}`, + ); + } + + return filtered; +} + +function match({ type, value }: Filter, endpoint: Endpoint): boolean { + switch (type) { + case 'resource': { + const regexStr = '^' + normalizeResource(value).replace(/\*/g, '.*') + '$'; + const regex = new RegExp(regexStr); + return regex.test(normalizeResource(endpoint.metadata.resource)); + } + case 'operation': + return endpoint.metadata.operation === value; + case 'tag': + return endpoint.metadata.tags.includes(value); + case 'tool': + return endpoint.tool.name === value; + } +} + +function normalizeResource(resource: string): string { + return resource.toLowerCase().replace(/[^a-z.*\-_]*/g, ''); +} diff --git a/packages/mcp-server/src/tools/types.ts b/packages/mcp-server/src/tools/types.ts new file mode 100644 index 00000000..8106d499 --- /dev/null +++ b/packages/mcp-server/src/tools/types.ts @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +type TextContentBlock = { + type: 'text'; + text: string; +}; + +type ImageContentBlock = { + type: 'image'; + data: string; + mimeType: string; +}; + +type AudioContentBlock = { + type: 'audio'; + data: string; + mimeType: string; +}; + +type ResourceContentBlock = { + type: 'resource'; + resource: + | { + uri: string; + mimeType: string; + text: string; + } + | { + uri: string; + mimeType: string; + blob: string; + }; +}; + +export type ContentBlock = TextContentBlock | ImageContentBlock | AudioContentBlock | ResourceContentBlock; + +export type ToolCallResult = { + content: ContentBlock[]; + isError?: boolean; +}; + +export type HandlerFunction = ( + client: ImageKit, + args: Record | undefined, +) => Promise; + +export function asTextContentResult(result: unknown): ToolCallResult { + return { + content: [ + { + type: 'text', + text: JSON.stringify(result, null, 2), + }, + ], + }; +} + +export async function asBinaryContentResult(response: Response): Promise { + const blob = await response.blob(); + const mimeType = blob.type; + const data = Buffer.from(await blob.arrayBuffer()).toString('base64'); + if (mimeType.startsWith('image/')) { + return { + content: [{ type: 'image', mimeType, data }], + }; + } else if (mimeType.startsWith('audio/')) { + return { + content: [{ type: 'audio', mimeType, data }], + }; + } else { + return { + content: [ + { + type: 'resource', + resource: { + // We must give a URI, even though this isn't actually an MCP resource. + uri: 'resource://tool-response', + mimeType, + blob: data, + }, + }, + ], + }; + } +} + +export type Metadata = { + resource: string; + operation: 'read' | 'write'; + tags: string[]; + httpMethod?: string; + httpPath?: string; + operationId?: string; +}; + +export type Endpoint = { + metadata: Metadata; + tool: Tool; + handler: HandlerFunction; +}; diff --git a/packages/mcp-server/tests/compat.test.ts b/packages/mcp-server/tests/compat.test.ts new file mode 100644 index 00000000..d6272f6c --- /dev/null +++ b/packages/mcp-server/tests/compat.test.ts @@ -0,0 +1,1166 @@ +import { + truncateToolNames, + removeTopLevelUnions, + removeAnyOf, + inlineRefs, + applyCompatibilityTransformations, + removeFormats, + findUsedDefs, +} from '../src/compat'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { JSONSchema } from '../src/compat'; +import { Endpoint } from '../src/tools'; + +describe('truncateToolNames', () => { + it('should return original names when maxLength is 0 or negative', () => { + const names = ['tool1', 'tool2', 'tool3']; + expect(truncateToolNames(names, 0)).toEqual(new Map()); + expect(truncateToolNames(names, -1)).toEqual(new Map()); + }); + + it('should return original names when all names are shorter than maxLength', () => { + const names = ['tool1', 'tool2', 'tool3']; + expect(truncateToolNames(names, 10)).toEqual(new Map()); + }); + + it('should truncate names longer than maxLength', () => { + const names = ['very-long-tool-name', 'another-long-tool-name', 'short']; + expect(truncateToolNames(names, 10)).toEqual( + new Map([ + ['very-long-tool-name', 'very-long-'], + ['another-long-tool-name', 'another-lo'], + ]), + ); + }); + + it('should handle duplicate truncated names by appending numbers', () => { + const names = ['tool-name-a', 'tool-name-b', 'tool-name-c']; + expect(truncateToolNames(names, 8)).toEqual( + new Map([ + ['tool-name-a', 'tool-na1'], + ['tool-name-b', 'tool-na2'], + ['tool-name-c', 'tool-na3'], + ]), + ); + }); +}); + +describe('removeTopLevelUnions', () => { + const createTestTool = (overrides = {}): Tool => ({ + name: 'test-tool', + description: 'Test tool', + inputSchema: { + type: 'object', + properties: {}, + }, + ...overrides, + }); + + it('should return the original tool if it has no anyOf at the top level', () => { + const tool = createTestTool({ + inputSchema: { + type: 'object', + properties: { + foo: { type: 'string' }, + }, + }, + }); + + expect(removeTopLevelUnions(tool)).toEqual([tool]); + }); + + it('should split a tool with top-level anyOf into multiple tools', () => { + const tool = createTestTool({ + name: 'union-tool', + description: 'A tool with unions', + inputSchema: { + type: 'object', + properties: { + common: { type: 'string' }, + }, + anyOf: [ + { + title: 'first variant', + description: 'Its the first variant', + properties: { + variant1: { type: 'string' }, + }, + required: ['variant1'], + }, + { + title: 'second variant', + properties: { + variant2: { type: 'number' }, + }, + required: ['variant2'], + }, + ], + }, + }); + + const result = removeTopLevelUnions(tool); + + expect(result).toEqual([ + { + name: 'union-tool_first_variant', + description: 'Its the first variant', + inputSchema: { + type: 'object', + title: 'first variant', + description: 'Its the first variant', + properties: { + common: { type: 'string' }, + variant1: { type: 'string' }, + }, + required: ['variant1'], + }, + }, + { + name: 'union-tool_second_variant', + description: 'A tool with unions', + inputSchema: { + type: 'object', + title: 'second variant', + description: 'A tool with unions', + properties: { + common: { type: 'string' }, + variant2: { type: 'number' }, + }, + required: ['variant2'], + }, + }, + ]); + }); + + it('should handle $defs and only include those used by the variant', () => { + const tool = createTestTool({ + name: 'defs-tool', + description: 'A tool with $defs', + inputSchema: { + type: 'object', + properties: { + common: { type: 'string' }, + }, + $defs: { + def1: { type: 'string', format: 'email' }, + def2: { type: 'number', minimum: 0 }, + unused: { type: 'boolean' }, + }, + anyOf: [ + { + properties: { + email: { $ref: '#/$defs/def1' }, + }, + }, + { + properties: { + count: { $ref: '#/$defs/def2' }, + }, + }, + ], + }, + }); + + const result = removeTopLevelUnions(tool); + + expect(result).toEqual([ + { + name: 'defs-tool_variant1', + description: 'A tool with $defs', + inputSchema: { + type: 'object', + description: 'A tool with $defs', + properties: { + common: { type: 'string' }, + email: { $ref: '#/$defs/def1' }, + }, + $defs: { + def1: { type: 'string', format: 'email' }, + }, + }, + }, + { + name: 'defs-tool_variant2', + description: 'A tool with $defs', + inputSchema: { + type: 'object', + description: 'A tool with $defs', + properties: { + common: { type: 'string' }, + count: { $ref: '#/$defs/def2' }, + }, + $defs: { + def2: { type: 'number', minimum: 0 }, + }, + }, + }, + ]); + }); +}); + +describe('removeAnyOf', () => { + it('should return original schema if it has no anyOf', () => { + const schema = { + type: 'object', + properties: { + foo: { type: 'string' }, + bar: { type: 'number' }, + }, + }; + + expect(removeAnyOf(schema)).toEqual(schema); + }); + + it('should remove anyOf field and use the first variant', () => { + const schema = { + type: 'object', + properties: { + common: { type: 'string' }, + }, + anyOf: [ + { + properties: { + variant1: { type: 'string' }, + }, + required: ['variant1'], + }, + { + properties: { + variant2: { type: 'number' }, + }, + required: ['variant2'], + }, + ], + }; + + const expected = { + type: 'object', + properties: { + common: { type: 'string' }, + variant1: { type: 'string' }, + }, + required: ['variant1'], + }; + + expect(removeAnyOf(schema)).toEqual(expected); + }); + + it('should recursively remove anyOf fields from nested properties', () => { + const schema = { + type: 'object', + properties: { + foo: { type: 'string' }, + nested: { + type: 'object', + properties: { + bar: { type: 'number' }, + }, + anyOf: [ + { + properties: { + option1: { type: 'boolean' }, + }, + }, + { + properties: { + option2: { type: 'array' }, + }, + }, + ], + }, + }, + }; + + const expected = { + type: 'object', + properties: { + foo: { type: 'string' }, + nested: { + type: 'object', + properties: { + bar: { type: 'number' }, + option1: { type: 'boolean' }, + }, + }, + }, + }; + + expect(removeAnyOf(schema)).toEqual(expected); + }); + + it('should handle arrays', () => { + const schema = { + type: 'object', + properties: { + items: { + type: 'array', + items: { + anyOf: [{ type: 'string' }, { type: 'number' }], + }, + }, + }, + }; + + const expected = { + type: 'object', + properties: { + items: { + type: 'array', + items: { + type: 'string', + }, + }, + }, + }; + + expect(removeAnyOf(schema)).toEqual(expected); + }); +}); + +describe('findUsedDefs', () => { + it('should handle circular references without stack overflow', () => { + const defs = { + person: { + type: 'object', + properties: { + name: { type: 'string' }, + friend: { $ref: '#/$defs/person' }, // Circular reference + }, + }, + }; + + const schema = { + type: 'object', + properties: { + user: { $ref: '#/$defs/person' }, + }, + }; + + // This should not throw a stack overflow error + expect(() => { + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('person'); + }).not.toThrow(); + }); + + it('should handle indirect circular references without stack overflow', () => { + const defs = { + node: { + type: 'object', + properties: { + value: { type: 'string' }, + child: { $ref: '#/$defs/childNode' }, + }, + }, + childNode: { + type: 'object', + properties: { + value: { type: 'string' }, + parent: { $ref: '#/$defs/node' }, // Indirect circular reference + }, + }, + }; + + const schema = { + type: 'object', + properties: { + root: { $ref: '#/$defs/node' }, + }, + }; + + // This should not throw a stack overflow error + expect(() => { + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('node'); + expect(result).toHaveProperty('childNode'); + }).not.toThrow(); + }); + + it('should find all used definitions in non-circular schemas', () => { + const defs = { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + address: { $ref: '#/$defs/address' }, + }, + }, + address: { + type: 'object', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + }, + unused: { + type: 'object', + properties: { + data: { type: 'string' }, + }, + }, + }; + + const schema = { + type: 'object', + properties: { + person: { $ref: '#/$defs/user' }, + }, + }; + + const result = findUsedDefs(schema, defs); + expect(result).toHaveProperty('user'); + expect(result).toHaveProperty('address'); + expect(result).not.toHaveProperty('unused'); + }); +}); + +describe('inlineRefs', () => { + it('should return the original schema if it does not contain $refs', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' }, + }, + }; + + expect(inlineRefs(schema)).toEqual(schema); + }); + + it('should inline simple $refs', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + user: { $ref: '#/$defs/user' }, + }, + $defs: { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + }, + }; + + const expected: JSONSchema = { + type: 'object', + properties: { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + }, + }; + + expect(inlineRefs(schema)).toEqual(expected); + }); + + it('should inline nested $refs', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + order: { $ref: '#/$defs/order' }, + }, + $defs: { + order: { + type: 'object', + properties: { + id: { type: 'string' }, + items: { type: 'array', items: { $ref: '#/$defs/item' } }, + }, + }, + item: { + type: 'object', + properties: { + product: { type: 'string' }, + quantity: { type: 'integer' }, + }, + }, + }, + }; + + const expected: JSONSchema = { + type: 'object', + properties: { + order: { + type: 'object', + properties: { + id: { type: 'string' }, + items: { + type: 'array', + items: { + type: 'object', + properties: { + product: { type: 'string' }, + quantity: { type: 'integer' }, + }, + }, + }, + }, + }, + }, + }; + + expect(inlineRefs(schema)).toEqual(expected); + }); + + it('should handle circular references by removing the circular part', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + person: { $ref: '#/$defs/person' }, + }, + $defs: { + person: { + type: 'object', + properties: { + name: { type: 'string' }, + friend: { $ref: '#/$defs/person' }, // Circular reference + }, + }, + }, + }; + + const expected: JSONSchema = { + type: 'object', + properties: { + person: { + type: 'object', + properties: { + name: { type: 'string' }, + // friend property is removed to break the circular reference + }, + }, + }, + }; + + expect(inlineRefs(schema)).toEqual(expected); + }); + + it('should handle indirect circular references', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + node: { $ref: '#/$defs/node' }, + }, + $defs: { + node: { + type: 'object', + properties: { + value: { type: 'string' }, + child: { $ref: '#/$defs/childNode' }, + }, + }, + childNode: { + type: 'object', + properties: { + value: { type: 'string' }, + parent: { $ref: '#/$defs/node' }, // Circular reference through childNode + }, + }, + }, + }; + + const expected: JSONSchema = { + type: 'object', + properties: { + node: { + type: 'object', + properties: { + value: { type: 'string' }, + child: { + type: 'object', + properties: { + value: { type: 'string' }, + // parent property is removed to break the circular reference + }, + }, + }, + }, + }, + }; + + expect(inlineRefs(schema)).toEqual(expected); + }); + + it('should preserve other properties when inlining references', () => { + const schema: JSONSchema = { + type: 'object', + properties: { + address: { $ref: '#/$defs/address', description: 'User address' }, + }, + $defs: { + address: { + type: 'object', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + required: ['street'], + }, + }, + }; + + const expected: JSONSchema = { + type: 'object', + properties: { + address: { + type: 'object', + description: 'User address', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + required: ['street'], + }, + }, + }; + + expect(inlineRefs(schema)).toEqual(expected); + }); +}); + +describe('removeFormats', () => { + it('should return original schema if formats capability is true', () => { + const schema = { + type: 'object', + properties: { + date: { type: 'string', description: 'A date field', format: 'date' }, + email: { type: 'string', description: 'An email field', format: 'email' }, + }, + }; + + expect(removeFormats(schema, true)).toEqual(schema); + }); + + it('should move format to description when formats capability is false', () => { + const schema = { + type: 'object', + properties: { + date: { type: 'string', description: 'A date field', format: 'date' }, + email: { type: 'string', description: 'An email field', format: 'email' }, + }, + }; + + const expected = { + type: 'object', + properties: { + date: { type: 'string', description: 'A date field (format: "date")' }, + email: { type: 'string', description: 'An email field (format: "email")' }, + }, + }; + + expect(removeFormats(schema, false)).toEqual(expected); + }); + + it('should handle properties without description', () => { + const schema = { + type: 'object', + properties: { + date: { type: 'string', format: 'date' }, + }, + }; + + const expected = { + type: 'object', + properties: { + date: { type: 'string', description: '(format: "date")' }, + }, + }; + + expect(removeFormats(schema, false)).toEqual(expected); + }); + + it('should handle nested properties', () => { + const schema = { + type: 'object', + properties: { + user: { + type: 'object', + properties: { + created_at: { type: 'string', description: 'Creation date', format: 'date-time' }, + }, + }, + }, + }; + + const expected = { + type: 'object', + properties: { + user: { + type: 'object', + properties: { + created_at: { type: 'string', description: 'Creation date (format: "date-time")' }, + }, + }, + }, + }; + + expect(removeFormats(schema, false)).toEqual(expected); + }); + + it('should handle arrays of objects', () => { + const schema = { + type: 'object', + properties: { + dates: { + type: 'array', + items: { + type: 'object', + properties: { + start: { type: 'string', description: 'Start date', format: 'date' }, + end: { type: 'string', description: 'End date', format: 'date' }, + }, + }, + }, + }, + }; + + const expected = { + type: 'object', + properties: { + dates: { + type: 'array', + items: { + type: 'object', + properties: { + start: { type: 'string', description: 'Start date (format: "date")' }, + end: { type: 'string', description: 'End date (format: "date")' }, + }, + }, + }, + }, + }; + + expect(removeFormats(schema, false)).toEqual(expected); + }); + + it('should handle schemas with $defs', () => { + const schema = { + type: 'object', + properties: { + date: { type: 'string', description: 'A date field', format: 'date' }, + }, + $defs: { + timestamp: { + type: 'string', + description: 'A timestamp field', + format: 'date-time', + }, + }, + }; + + const expected = { + type: 'object', + properties: { + date: { type: 'string', description: 'A date field (format: "date")' }, + }, + $defs: { + timestamp: { + type: 'string', + description: 'A timestamp field (format: "date-time")', + }, + }, + }; + + expect(removeFormats(schema, false)).toEqual(expected); + }); +}); + +describe('applyCompatibilityTransformations', () => { + const createTestTool = (name: string, overrides = {}): Tool => ({ + name, + description: 'Test tool', + inputSchema: { + type: 'object', + properties: {}, + }, + ...overrides, + }); + + const createTestEndpoint = (tool: Tool): Endpoint => ({ + tool, + handler: jest.fn(), + metadata: { + resource: 'test', + operation: 'read' as const, + tags: [], + }, + }); + + it('should not modify endpoints when all capabilities are enabled', () => { + const tool = createTestTool('test-tool'); + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + expect(transformed).toEqual(endpoints); + }); + + it('should split tools with top-level unions when topLevelUnions is disabled', () => { + const tool = createTestTool('union-tool', { + inputSchema: { + type: 'object', + properties: { + common: { type: 'string' }, + }, + anyOf: [ + { + title: 'first variant', + properties: { + variant1: { type: 'string' }, + }, + }, + { + title: 'second variant', + properties: { + variant2: { type: 'number' }, + }, + }, + ], + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + expect(transformed.length).toBe(2); + expect(transformed[0]!.tool.name).toBe('union-tool_first_variant'); + expect(transformed[1]!.tool.name).toBe('union-tool_second_variant'); + }); + + it('should handle variants without titles in removeTopLevelUnions', () => { + const tool = createTestTool('union-tool', { + inputSchema: { + type: 'object', + properties: { + common: { type: 'string' }, + }, + anyOf: [ + { + properties: { + variant1: { type: 'string' }, + }, + }, + { + properties: { + variant2: { type: 'number' }, + }, + }, + ], + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + expect(transformed.length).toBe(2); + expect(transformed[0]!.tool.name).toBe('union-tool_variant1'); + expect(transformed[1]!.tool.name).toBe('union-tool_variant2'); + }); + + it('should truncate tool names when toolNameLength is set', () => { + const tools = [ + createTestTool('very-long-tool-name-that-exceeds-limit'), + createTestTool('another-long-tool-name-to-truncate'), + createTestTool('short-name'), + ]; + + const endpoints = tools.map(createTestEndpoint); + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: 20, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + expect(transformed[0]!.tool.name).toBe('very-long-tool-name-'); + expect(transformed[1]!.tool.name).toBe('another-long-tool-na'); + expect(transformed[2]!.tool.name).toBe('short-name'); + }); + + it('should inline refs when refs capability is disabled', () => { + const tool = createTestTool('ref-tool', { + inputSchema: { + type: 'object', + properties: { + user: { $ref: '#/$defs/user' }, + }, + $defs: { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }, + }, + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: false, + unions: true, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + const schema = transformed[0]!.tool.inputSchema as JSONSchema; + expect(schema.$defs).toBeUndefined(); + + if (schema.properties) { + expect(schema.properties['user']).toEqual({ + type: 'object', + properties: { + name: { type: 'string' }, + email: { type: 'string' }, + }, + }); + } + }); + + it('should preserve external refs when inlining', () => { + const tool = createTestTool('ref-tool', { + inputSchema: { + type: 'object', + properties: { + internal: { $ref: '#/$defs/internal' }, + external: { $ref: 'https://example.com/schemas/external.json' }, + }, + $defs: { + internal: { + type: 'object', + properties: { + name: { type: 'string' }, + }, + }, + }, + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: false, + unions: true, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + const schema = transformed[0]!.tool.inputSchema as JSONSchema; + + if (schema.properties) { + expect(schema.properties['internal']).toEqual({ + type: 'object', + properties: { + name: { type: 'string' }, + }, + }); + expect(schema.properties['external']).toEqual({ + $ref: 'https://example.com/schemas/external.json', + }); + } + }); + + it('should remove anyOf fields when unions capability is disabled', () => { + const tool = createTestTool('union-tool', { + inputSchema: { + type: 'object', + properties: { + field: { + anyOf: [{ type: 'string' }, { type: 'number' }], + }, + }, + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: true, + unions: false, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + const schema = transformed[0]!.tool.inputSchema as JSONSchema; + + if (schema.properties && schema.properties['field']) { + const field = schema.properties['field']; + expect(field.anyOf).toBeUndefined(); + expect(field.type).toBe('string'); + } + }); + + it('should correctly combine topLevelUnions and toolNameLength transformations', () => { + const tool = createTestTool('very-long-union-tool-name', { + inputSchema: { + type: 'object', + properties: { + common: { type: 'string' }, + }, + anyOf: [ + { + title: 'first variant', + properties: { + variant1: { type: 'string' }, + }, + }, + { + title: 'second variant', + properties: { + variant2: { type: 'number' }, + }, + }, + ], + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: false, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: 20, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + expect(transformed.length).toBe(2); + + // Both names should be truncated because they exceed 20 characters + expect(transformed[0]!.tool.name).toBe('very-long-union-too1'); + expect(transformed[1]!.tool.name).toBe('very-long-union-too2'); + }); + + it('should correctly combine refs and unions transformations', () => { + const tool = createTestTool('complex-tool', { + inputSchema: { + type: 'object', + properties: { + user: { $ref: '#/$defs/user' }, + }, + $defs: { + user: { + type: 'object', + properties: { + preference: { + anyOf: [{ type: 'string' }, { type: 'number' }], + }, + }, + }, + }, + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: false, + unions: false, + formats: true, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + const schema = transformed[0]!.tool.inputSchema as JSONSchema; + + // Refs should be inlined + expect(schema.$defs).toBeUndefined(); + + // Safely access nested properties + if (schema.properties && schema.properties['user']) { + const user = schema.properties['user']; + // User should be inlined + expect(user.type).toBe('object'); + + // AnyOf in the inlined user.preference should be removed + if (user.properties && user.properties['preference']) { + const preference = user.properties['preference']; + expect(preference.anyOf).toBeUndefined(); + expect(preference.type).toBe('string'); + } + } + }); + + it('should handle formats capability being false', () => { + const tool = createTestTool('format-tool', { + inputSchema: { + type: 'object', + properties: { + date: { type: 'string', description: 'A date', format: 'date' }, + }, + }, + }); + + const endpoints = [createTestEndpoint(tool)]; + + const capabilities = { + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: false, + toolNameLength: undefined, + }; + + const transformed = applyCompatibilityTransformations(endpoints, capabilities); + const schema = transformed[0]!.tool.inputSchema as JSONSchema; + + if (schema.properties && schema.properties['date']) { + const dateField = schema.properties['date']; + expect(dateField['format']).toBeUndefined(); + expect(dateField['description']).toBe('A date (format: "date")'); + } + }); +}); diff --git a/packages/mcp-server/tests/dynamic-tools.test.ts b/packages/mcp-server/tests/dynamic-tools.test.ts new file mode 100644 index 00000000..08963af8 --- /dev/null +++ b/packages/mcp-server/tests/dynamic-tools.test.ts @@ -0,0 +1,185 @@ +import { dynamicTools } from '../src/dynamic-tools'; +import { Endpoint } from '../src/tools'; + +describe('dynamicTools', () => { + const fakeClient = {} as any; + + const endpoints: Endpoint[] = [ + makeEndpoint('test_read_endpoint', 'test_resource', 'read', ['test']), + makeEndpoint('test_write_endpoint', 'test_resource', 'write', ['test']), + makeEndpoint('user_endpoint', 'user', 'read', ['user', 'admin']), + makeEndpoint('admin_endpoint', 'admin', 'write', ['admin']), + ]; + + const tools = dynamicTools(endpoints); + + const toolsMap = { + list_api_endpoints: toolOrError('list_api_endpoints'), + get_api_endpoint_schema: toolOrError('get_api_endpoint_schema'), + invoke_api_endpoint: toolOrError('invoke_api_endpoint'), + }; + + describe('list_api_endpoints', () => { + it('should return all endpoints when no search query is provided', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, {}); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(endpoints.length); + expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_read_endpoint'); + expect(result.tools.map((t: { name: string }) => t.name)).toContain('test_write_endpoint'); + expect(result.tools.map((t: { name: string }) => t.name)).toContain('user_endpoint'); + expect(result.tools.map((t: { name: string }) => t.name)).toContain('admin_endpoint'); + }); + + it('should filter endpoints by name', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'user' }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe('user_endpoint'); + }); + + it('should filter endpoints by resource', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools.some((t: { resource: string }) => t.resource === 'admin')).toBeTruthy(); + }); + + it('should filter endpoints by tag', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'admin' }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools.some((t: { tags: string[] }) => t.tags.includes('admin'))).toBeTruthy(); + }); + + it('should be case insensitive in search', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { search_query: 'ADMIN' }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools.length).toBe(2); + result.tools.forEach((tool: { name: string; resource: string; tags: string[] }) => { + expect( + tool.name.toLowerCase().includes('admin') || + tool.resource.toLowerCase().includes('admin') || + tool.tags.some((tag: string) => tag.toLowerCase().includes('admin')), + ).toBeTruthy(); + }); + }); + + it('should filter endpoints by description', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { + search_query: 'Test endpoint for user_endpoint', + }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe('user_endpoint'); + expect(result.tools[0].description).toBe('Test endpoint for user_endpoint'); + }); + + it('should filter endpoints by partial description match', async () => { + const content = await toolsMap.list_api_endpoints.handler(fakeClient, { + search_query: 'endpoint for user', + }); + const result = JSON.parse(content.content[0].text); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe('user_endpoint'); + }); + }); + + describe('get_api_endpoint_schema', () => { + it('should return schema for existing endpoint', async () => { + const content = await toolsMap.get_api_endpoint_schema.handler(fakeClient, { + endpoint: 'test_read_endpoint', + }); + const result = JSON.parse(content.content[0].text); + + expect(result).toEqual(endpoints[0]?.tool); + }); + + it('should throw error for non-existent endpoint', async () => { + await expect( + toolsMap.get_api_endpoint_schema.handler(fakeClient, { endpoint: 'non_existent_endpoint' }), + ).rejects.toThrow('Endpoint non_existent_endpoint not found'); + }); + + it('should throw error when no endpoint provided', async () => { + await expect(toolsMap.get_api_endpoint_schema.handler(fakeClient, undefined)).rejects.toThrow( + 'No endpoint provided', + ); + }); + }); + + describe('invoke_api_endpoint', () => { + it('should successfully invoke endpoint with valid arguments', async () => { + const mockHandler = endpoints[0]?.handler as jest.Mock; + mockHandler.mockClear(); + + await toolsMap.invoke_api_endpoint.handler(fakeClient, { + endpoint_name: 'test_read_endpoint', + args: { testParam: 'test value' }, + }); + + expect(mockHandler).toHaveBeenCalledWith(fakeClient, { testParam: 'test value' }); + }); + + it('should throw error for non-existent endpoint', async () => { + await expect( + toolsMap.invoke_api_endpoint.handler(fakeClient, { + endpoint_name: 'non_existent_endpoint', + args: { testParam: 'test value' }, + }), + ).rejects.toThrow(/Endpoint non_existent_endpoint not found/); + }); + + it('should throw error when no arguments provided', async () => { + await expect(toolsMap.invoke_api_endpoint.handler(fakeClient, undefined)).rejects.toThrow( + 'No endpoint provided', + ); + }); + + it('should throw error for invalid argument schema', async () => { + await expect( + toolsMap.invoke_api_endpoint.handler(fakeClient, { + endpoint_name: 'test_read_endpoint', + args: { wrongParam: 'test value' }, // Missing required testParam + }), + ).rejects.toThrow(/Invalid arguments for endpoint/); + }); + }); + + function toolOrError(name: string) { + const tool = tools.find((tool) => tool.tool.name === name); + if (!tool) throw new Error(`Tool ${name} not found`); + return tool; + } +}); + +function makeEndpoint( + name: string, + resource: string, + operation: 'read' | 'write', + tags: string[] = [], +): Endpoint { + return { + metadata: { + resource, + operation, + tags, + }, + tool: { + name, + description: `Test endpoint for ${name}`, + inputSchema: { + type: 'object', + properties: { + testParam: { type: 'string' }, + }, + required: ['testParam'], + }, + }, + handler: jest.fn().mockResolvedValue({ success: true }), + }; +} diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts new file mode 100644 index 00000000..a8a5b81a --- /dev/null +++ b/packages/mcp-server/tests/options.test.ts @@ -0,0 +1,518 @@ +import { parseCLIOptions, parseQueryOptions } from '../src/options'; +import { Filter } from '../src/tools'; +import { parseEmbeddedJSON } from '../src/compat'; + +// Mock process.argv +const mockArgv = (args: string[]) => { + const originalArgv = process.argv; + process.argv = ['node', 'test.js', ...args]; + return () => { + process.argv = originalArgv; + }; +}; + +describe('parseCLIOptions', () => { + it('should parse basic filter options', () => { + const cleanup = mockArgv([ + '--tool=test-tool', + '--resource=test-resource', + '--operation=read', + '--tag=test-tag', + ]); + + const result = parseCLIOptions(); + + expect(result.filters).toEqual([ + { type: 'tag', op: 'include', value: 'test-tag' }, + { type: 'resource', op: 'include', value: 'test-resource' }, + { type: 'tool', op: 'include', value: 'test-tool' }, + { type: 'operation', op: 'include', value: 'read' }, + ] as Filter[]); + + expect(result.capabilities).toEqual({}); + + expect(result.list).toBe(false); + + cleanup(); + }); + + it('should parse exclusion filters', () => { + const cleanup = mockArgv([ + '--no-tool=exclude-tool', + '--no-resource=exclude-resource', + '--no-operation=write', + '--no-tag=exclude-tag', + ]); + + const result = parseCLIOptions(); + + expect(result.filters).toEqual([ + { type: 'tag', op: 'exclude', value: 'exclude-tag' }, + { type: 'resource', op: 'exclude', value: 'exclude-resource' }, + { type: 'tool', op: 'exclude', value: 'exclude-tool' }, + { type: 'operation', op: 'exclude', value: 'write' }, + ] as Filter[]); + + expect(result.capabilities).toEqual({}); + + cleanup(); + }); + + it('should parse client presets', () => { + const cleanup = mockArgv(['--client=openai-agents']); + + const result = parseCLIOptions(); + + expect(result.client).toEqual('openai-agents'); + + cleanup(); + }); + + it('should parse individual capabilities', () => { + const cleanup = mockArgv([ + '--capability=top-level-unions', + '--capability=valid-json', + '--capability=refs', + '--capability=unions', + '--capability=tool-name-length=40', + ]); + + const result = parseCLIOptions(); + + expect(result.capabilities).toEqual({ + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + toolNameLength: 40, + }); + + cleanup(); + }); + + it('should handle list option', () => { + const cleanup = mockArgv(['--list']); + + const result = parseCLIOptions(); + + expect(result.list).toBe(true); + + cleanup(); + }); + + it('should handle multiple filters of the same type', () => { + const cleanup = mockArgv(['--tool=tool1', '--tool=tool2', '--resource=res1', '--resource=res2']); + + const result = parseCLIOptions(); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'include', value: 'res1' }, + { type: 'resource', op: 'include', value: 'res2' }, + { type: 'tool', op: 'include', value: 'tool1' }, + { type: 'tool', op: 'include', value: 'tool2' }, + ] as Filter[]); + + cleanup(); + }); + + it('should handle comma-separated values in array options', () => { + const cleanup = mockArgv([ + '--tool=tool1,tool2', + '--resource=res1,res2', + '--capability=top-level-unions,valid-json,unions', + ]); + + const result = parseCLIOptions(); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'include', value: 'res1' }, + { type: 'resource', op: 'include', value: 'res2' }, + { type: 'tool', op: 'include', value: 'tool1' }, + { type: 'tool', op: 'include', value: 'tool2' }, + ] as Filter[]); + + expect(result.capabilities).toEqual({ + topLevelUnions: true, + validJson: true, + unions: true, + }); + + cleanup(); + }); + + it('should handle invalid tool-name-length format', () => { + const cleanup = mockArgv(['--capability=tool-name-length=invalid']); + + // Mock console.error to prevent output during test + const originalError = console.error; + console.error = jest.fn(); + + expect(() => parseCLIOptions()).toThrow(); + + console.error = originalError; + cleanup(); + }); + + it('should handle unknown capability', () => { + const cleanup = mockArgv(['--capability=unknown-capability']); + + // Mock console.error to prevent output during test + const originalError = console.error; + console.error = jest.fn(); + + expect(() => parseCLIOptions()).toThrow(); + + console.error = originalError; + cleanup(); + }); +}); + +describe('parseQueryOptions', () => { + const defaultOptions = { + client: undefined, + includeDynamicTools: undefined, + includeAllTools: undefined, + filters: [], + capabilities: { + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }, + }; + + it('should parse basic filter options from query string', () => { + const query = 'tool=test-tool&resource=test-resource&operation=read&tag=test-tag'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'include', value: 'test-resource' }, + { type: 'operation', op: 'include', value: 'read' }, + { type: 'tag', op: 'include', value: 'test-tag' }, + { type: 'tool', op: 'include', value: 'test-tool' }, + ]); + + expect(result.capabilities).toEqual({ + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: undefined, + }); + }); + + it('should parse exclusion filters from query string', () => { + const query = 'no_tool=exclude-tool&no_resource=exclude-resource&no_operation=write&no_tag=exclude-tag'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'exclude', value: 'exclude-resource' }, + { type: 'operation', op: 'exclude', value: 'write' }, + { type: 'tag', op: 'exclude', value: 'exclude-tag' }, + { type: 'tool', op: 'exclude', value: 'exclude-tool' }, + ]); + }); + + it('should parse client option from query string', () => { + const query = 'client=openai-agents'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.client).toBe('openai-agents'); + }); + + it('should parse client capabilities from query string', () => { + const query = 'capability=top-level-unions&capability=valid-json&capability=tool-name-length%3D40'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.capabilities).toEqual({ + topLevelUnions: true, + validJson: true, + refs: true, + unions: true, + formats: true, + toolNameLength: 40, + }); + }); + + it('should parse no-capability options from query string', () => { + const query = 'no_capability=top-level-unions&no_capability=refs&no_capability=formats'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.capabilities).toEqual({ + topLevelUnions: false, + validJson: true, + refs: false, + unions: true, + formats: false, + toolNameLength: undefined, + }); + }); + + it('should parse tools options from query string', () => { + const query = 'tools=dynamic&tools=all'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.includeDynamicTools).toBe(true); + expect(result.includeAllTools).toBe(true); + }); + + it('should parse no-tools options from query string', () => { + const query = 'tools=dynamic&tools=all&no_tools=dynamic'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.includeDynamicTools).toBe(false); + expect(result.includeAllTools).toBe(true); + }); + + it('should handle array values in query string', () => { + const query = 'tool[]=tool1&tool[]=tool2&resource[]=res1&resource[]=res2'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'include', value: 'res1' }, + { type: 'resource', op: 'include', value: 'res2' }, + { type: 'tool', op: 'include', value: 'tool1' }, + { type: 'tool', op: 'include', value: 'tool2' }, + ]); + }); + + it('should merge with default options', () => { + const defaultWithFilters = { + ...defaultOptions, + filters: [{ type: 'tag' as const, op: 'include' as const, value: 'existing-tag' }], + client: 'cursor' as const, + includeDynamicTools: true, + }; + + const query = 'tool=new-tool&resource=new-resource'; + const result = parseQueryOptions(defaultWithFilters, query); + + expect(result.filters).toEqual([ + { type: 'tag', op: 'include', value: 'existing-tag' }, + { type: 'resource', op: 'include', value: 'new-resource' }, + { type: 'tool', op: 'include', value: 'new-tool' }, + ]); + + expect(result.client).toBe('cursor'); + expect(result.includeDynamicTools).toBe(true); + }); + + it('should override client from default options', () => { + const defaultWithClient = { + ...defaultOptions, + client: 'cursor' as const, + }; + + const query = 'client=openai-agents'; + const result = parseQueryOptions(defaultWithClient, query); + + expect(result.client).toBe('openai-agents'); + }); + + it('should merge capabilities with default options', () => { + const defaultWithCapabilities = { + ...defaultOptions, + capabilities: { + topLevelUnions: false, + validJson: false, + refs: true, + unions: true, + formats: true, + toolNameLength: 30, + }, + }; + + const query = 'capability=top-level-unions&no_capability=refs'; + const result = parseQueryOptions(defaultWithCapabilities, query); + + expect(result.capabilities).toEqual({ + topLevelUnions: true, + validJson: false, + refs: false, + unions: true, + formats: true, + toolNameLength: 30, + }); + }); + + it('should handle empty query string', () => { + const query = ''; + const result = parseQueryOptions(defaultOptions, query); + + expect(result).toEqual(defaultOptions); + }); + + it('should handle invalid query string gracefully', () => { + const query = 'invalid=value&operation=invalid-operation'; + + // Should throw due to Zod validation for invalid operation + expect(() => parseQueryOptions(defaultOptions, query)).toThrow(); + }); + + it('should preserve default undefined values when not specified', () => { + const defaultWithUndefined = { + ...defaultOptions, + client: undefined, + includeDynamicTools: undefined, + includeAllTools: undefined, + }; + + const query = 'tool=test-tool'; + const result = parseQueryOptions(defaultWithUndefined, query); + + expect(result.client).toBeUndefined(); + expect(result.includeDynamicTools).toBeFalsy(); + expect(result.includeAllTools).toBeFalsy(); + }); + + it('should handle complex query with mixed include and exclude filters', () => { + const query = + 'tool=include-tool&no_tool=exclude-tool&resource=include-res&no_resource=exclude-res&operation=read&tag=include-tag&no_tag=exclude-tag'; + const result = parseQueryOptions(defaultOptions, query); + + expect(result.filters).toEqual([ + { type: 'resource', op: 'include', value: 'include-res' }, + { type: 'operation', op: 'include', value: 'read' }, + { type: 'tag', op: 'include', value: 'include-tag' }, + { type: 'tool', op: 'include', value: 'include-tool' }, + { type: 'resource', op: 'exclude', value: 'exclude-res' }, + { type: 'tag', op: 'exclude', value: 'exclude-tag' }, + { type: 'tool', op: 'exclude', value: 'exclude-tool' }, + ]); + }); +}); + +describe('parseEmbeddedJSON', () => { + it('should not change non-string values', () => { + const args = { + numberProp: 42, + booleanProp: true, + objectProp: { nested: 'value' }, + arrayProp: [1, 2, 3], + nullProp: null, + undefinedProp: undefined, + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).toBe(args); // Should return original object since no changes made + expect(result['numberProp']).toBe(42); + expect(result['booleanProp']).toBe(true); + expect(result['objectProp']).toEqual({ nested: 'value' }); + expect(result['arrayProp']).toEqual([1, 2, 3]); + expect(result['nullProp']).toBe(null); + expect(result['undefinedProp']).toBe(undefined); + }); + + it('should parse valid JSON objects in string properties', () => { + const args = { + jsonObjectString: '{"key": "value", "number": 123}', + regularString: 'not json', + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).not.toBe(args); // Should return new object since changes were made + expect(result['jsonObjectString']).toEqual({ key: 'value', number: 123 }); + expect(result['regularString']).toBe('not json'); + }); + + it('should leave invalid JSON in string properties unchanged', () => { + const args = { + invalidJson1: '{"key": value}', // Missing quotes around value + invalidJson2: '{key: "value"}', // Missing quotes around key + invalidJson3: '{"key": "value",}', // Trailing comma + invalidJson4: 'just a regular string', + emptyString: '', + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).toBe(args); // Should return original object since no changes made + expect(result['invalidJson1']).toBe('{"key": value}'); + expect(result['invalidJson2']).toBe('{key: "value"}'); + expect(result['invalidJson3']).toBe('{"key": "value",}'); + expect(result['invalidJson4']).toBe('just a regular string'); + expect(result['emptyString']).toBe(''); + }); + + it('should not parse JSON primitives in string properties', () => { + const args = { + numberString: '123', + floatString: '45.67', + negativeNumberString: '-89', + booleanTrueString: 'true', + booleanFalseString: 'false', + nullString: 'null', + jsonArrayString: '[1, 2, 3, "test"]', + regularString: 'not json', + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).toBe(args); // Should return original object since no changes made + expect(result['numberString']).toBe('123'); + expect(result['floatString']).toBe('45.67'); + expect(result['negativeNumberString']).toBe('-89'); + expect(result['booleanTrueString']).toBe('true'); + expect(result['booleanFalseString']).toBe('false'); + expect(result['nullString']).toBe('null'); + expect(result['jsonArrayString']).toBe('[1, 2, 3, "test"]'); + expect(result['regularString']).toBe('not json'); + }); + + it('should handle mixed valid objects and other JSON types', () => { + const args = { + validObject: '{"success": true}', + invalidObject: '{"missing": quote}', + validNumber: '42', + validArray: '[1, 2, 3]', + keepAsString: 'hello world', + nonString: 123, + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).not.toBe(args); // Should return new object since some changes were made + expect(result['validObject']).toEqual({ success: true }); + expect(result['invalidObject']).toBe('{"missing": quote}'); + expect(result['validNumber']).toBe('42'); // Not parsed, remains string + expect(result['validArray']).toBe('[1, 2, 3]'); // Not parsed, remains string + expect(result['keepAsString']).toBe('hello world'); + expect(result['nonString']).toBe(123); + }); + + it('should return original object when no strings are present', () => { + const args = { + number: 42, + boolean: true, + object: { key: 'value' }, + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).toBe(args); // Should return original object since no changes made + }); + + it('should return original object when all strings are invalid JSON', () => { + const args = { + string1: 'hello', + string2: 'world', + string3: 'not json at all', + }; + const schema = {}; + + const result = parseEmbeddedJSON(args, schema); + + expect(result).toBe(args); // Should return original object since no changes made + }); +}); diff --git a/packages/mcp-server/tests/tools.test.ts b/packages/mcp-server/tests/tools.test.ts new file mode 100644 index 00000000..cfff24a2 --- /dev/null +++ b/packages/mcp-server/tests/tools.test.ts @@ -0,0 +1,225 @@ +import { Endpoint, Filter, Metadata, query } from '../src/tools'; + +describe('Endpoint filtering', () => { + const endpoints: Endpoint[] = [ + endpoint({ + resource: 'user', + operation: 'read', + tags: ['admin'], + toolName: 'retrieve_user', + }), + endpoint({ + resource: 'user.profile', + operation: 'write', + tags: [], + toolName: 'create_user_profile', + }), + endpoint({ + resource: 'user.profile', + operation: 'read', + tags: [], + toolName: 'get_user_profile', + }), + endpoint({ + resource: 'user.roles.permissions', + operation: 'write', + tags: ['admin', 'security'], + toolName: 'update_user_role_permissions', + }), + endpoint({ + resource: 'documents.metadata.tags', + operation: 'write', + tags: ['taxonomy', 'metadata'], + toolName: 'create_document_metadata_tags', + }), + endpoint({ + resource: 'organization.settings', + operation: 'read', + tags: ['admin', 'configuration'], + toolName: 'get_organization_settings', + }), + ]; + + const tests: { name: string; filters: Filter[]; expected: string[] }[] = [ + { + name: 'match none', + filters: [], + expected: [], + }, + + // Resource tests + { + name: 'simple resource', + filters: [{ type: 'resource', op: 'include', value: 'user' }], + expected: ['retrieve_user'], + }, + { + name: 'exclude resource', + filters: [{ type: 'resource', op: 'exclude', value: 'user' }], + expected: [ + 'create_user_profile', + 'get_user_profile', + 'update_user_role_permissions', + 'create_document_metadata_tags', + 'get_organization_settings', + ], + }, + { + name: 'resource and subresources', + filters: [{ type: 'resource', op: 'include', value: 'user*' }], + expected: ['retrieve_user', 'create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + { + name: 'just subresources', + filters: [{ type: 'resource', op: 'include', value: 'user.*' }], + expected: ['create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + { + name: 'specific subresource', + filters: [{ type: 'resource', op: 'include', value: 'user.roles.permissions' }], + expected: ['update_user_role_permissions'], + }, + { + name: 'deep wildcard match', + filters: [{ type: 'resource', op: 'include', value: '*.*.tags' }], + expected: ['create_document_metadata_tags'], + }, + + // Operation tests + { + name: 'read operation', + filters: [{ type: 'operation', op: 'include', value: 'read' }], + expected: ['retrieve_user', 'get_user_profile', 'get_organization_settings'], + }, + { + name: 'write operation', + filters: [{ type: 'operation', op: 'include', value: 'write' }], + expected: ['create_user_profile', 'update_user_role_permissions', 'create_document_metadata_tags'], + }, + { + name: 'resource and operation combined', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'operation', op: 'exclude', value: 'write' }, + ], + expected: ['get_user_profile'], + }, + + // Tag tests + { + name: 'admin tag', + filters: [{ type: 'tag', op: 'include', value: 'admin' }], + expected: ['retrieve_user', 'update_user_role_permissions', 'get_organization_settings'], + }, + { + name: 'taxonomy tag', + filters: [{ type: 'tag', op: 'include', value: 'taxonomy' }], + expected: ['create_document_metadata_tags'], + }, + { + name: 'multiple tags (OR logic)', + filters: [ + { type: 'tag', op: 'include', value: 'admin' }, + { type: 'tag', op: 'include', value: 'security' }, + ], + expected: ['retrieve_user', 'update_user_role_permissions', 'get_organization_settings'], + }, + { + name: 'excluding a tag', + filters: [ + { type: 'tag', op: 'include', value: 'admin' }, + { type: 'tag', op: 'exclude', value: 'security' }, + ], + expected: ['retrieve_user', 'get_organization_settings'], + }, + + // Tool name tests + { + name: 'tool name match', + filters: [{ type: 'tool', op: 'include', value: 'get_organization_settings' }], + expected: ['get_organization_settings'], + }, + { + name: 'two tools match', + filters: [ + { type: 'tool', op: 'include', value: 'get_organization_settings' }, + { type: 'tool', op: 'include', value: 'create_user_profile' }, + ], + expected: ['create_user_profile', 'get_organization_settings'], + }, + { + name: 'excluding tool by name', + filters: [ + { type: 'resource', op: 'include', value: 'user*' }, + { type: 'tool', op: 'exclude', value: 'retrieve_user' }, + ], + expected: ['create_user_profile', 'get_user_profile', 'update_user_role_permissions'], + }, + + // Complex combinations + { + name: 'complex filter: read operations with admin tag', + filters: [ + { type: 'operation', op: 'include', value: 'read' }, + { type: 'tag', op: 'include', value: 'admin' }, + ], + expected: [ + 'retrieve_user', + 'get_user_profile', + 'update_user_role_permissions', + 'get_organization_settings', + ], + }, + { + name: 'complex filter: user resources with no tags', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'tag', op: 'exclude', value: 'admin' }, + ], + expected: ['create_user_profile', 'get_user_profile'], + }, + { + name: 'complex filter: user resources and tags', + filters: [ + { type: 'resource', op: 'include', value: 'user.profile' }, + { type: 'tag', op: 'include', value: 'admin' }, + ], + expected: [ + 'retrieve_user', + 'create_user_profile', + 'get_user_profile', + 'update_user_role_permissions', + 'get_organization_settings', + ], + }, + ]; + + tests.forEach((test) => { + it(`filters by ${test.name}`, () => { + const filtered = query(test.filters, endpoints); + expect(filtered.map((e) => e.tool.name)).toEqual(test.expected); + }); + }); +}); + +function endpoint({ + resource, + operation, + tags, + toolName, +}: { + resource: string; + operation: Metadata['operation']; + tags: string[]; + toolName: string; +}): Endpoint { + return { + metadata: { + resource, + operation, + tags, + }, + tool: { name: toolName, inputSchema: { type: 'object', properties: {} } }, + handler: jest.fn(), + }; +} diff --git a/packages/mcp-server/tsc-multi.json b/packages/mcp-server/tsc-multi.json new file mode 100644 index 00000000..4facad5a --- /dev/null +++ b/packages/mcp-server/tsc-multi.json @@ -0,0 +1,7 @@ +{ + "targets": [ + { "extname": ".js", "module": "commonjs" }, + { "extname": ".mjs", "module": "esnext" } + ], + "projects": ["tsconfig.build.json"] +} diff --git a/packages/mcp-server/tsconfig.build.json b/packages/mcp-server/tsconfig.build.json new file mode 100644 index 00000000..5111d3e2 --- /dev/null +++ b/packages/mcp-server/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "include": ["dist/src"], + "exclude": [], + "compilerOptions": { + "rootDir": "./dist/src", + "paths": { + "imagekit-api-mcp/*": ["dist/src/*"], + "imagekit-api-mcp": ["dist/src/index.ts"] + }, + "noEmit": false, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "pretty": true, + "sourceMap": true + } +} diff --git a/packages/mcp-server/tsconfig.dist-src.json b/packages/mcp-server/tsconfig.dist-src.json new file mode 100644 index 00000000..e9f2d70b --- /dev/null +++ b/packages/mcp-server/tsconfig.dist-src.json @@ -0,0 +1,11 @@ +{ + // this config is included in the published src directory to prevent TS errors + // from appearing when users go to source, and VSCode opens the source .ts file + // via declaration maps + "include": ["index.ts"], + "compilerOptions": { + "target": "es2015", + "lib": ["DOM"], + "moduleResolution": "node" + } +} diff --git a/packages/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json new file mode 100644 index 00000000..ddb25b3e --- /dev/null +++ b/packages/mcp-server/tsconfig.json @@ -0,0 +1,37 @@ +{ + "include": ["src", "tests", "examples"], + "exclude": [], + "compilerOptions": { + "target": "es2020", + "lib": ["es2020"], + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "baseUrl": "./", + "paths": { + "imagekit-api-mcp/*": ["src/*"], + "imagekit-api-mcp": ["src/index.ts"] + }, + "noEmit": true, + + "resolveJsonModule": true, + + "forceConsistentCasingInFileNames": true, + + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + + "skipLibCheck": true + } +} diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock new file mode 100644 index 00000000..ad819835 --- /dev/null +++ b/packages/mcp-server/yarn.lock @@ -0,0 +1,3916 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@anthropic-ai/dxt@^0.2.6": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@anthropic-ai/dxt/-/dxt-0.2.6.tgz#636197c3d083c9136ac3b5a11d2ba82477fdc2c6" + integrity sha512-5VSqKRpkytTYh5UJz9jOaI8zLXNCe4Gc+ArKGFV6IeWnEPP0Qnd0k+V3pO8cYzp92Puf/+Cgo0xc4haE0azTXg== + dependencies: + "@inquirer/prompts" "^6.0.1" + commander "^13.1.0" + fflate "^0.8.2" + galactus "^1.0.0" + ignore "^7.0.5" + node-forge "^1.3.1" + pretty-bytes "^5.6.0" + zod "^3.25.67" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.27.2.tgz#4183f9e642fd84e74e3eea7ffa93a412e3b102c9" + integrity sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.27.1.tgz#89de51e86bd12246003e3524704c49541b16c3e6" + integrity sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.27.1" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helpers" "^7.27.1" + "@babel/parser" "^7.27.1" + "@babel/template" "^7.27.1" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.27.1", "@babel/generator@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" + integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== + dependencies: + "@babel/parser" "^7.27.1" + "@babel/types" "^7.27.1" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.27.1": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== + dependencies: + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-module-transforms@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz#e1663b8b71d2de948da5c4fb2a20ca4f3ec27a6f" + integrity sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helpers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.1.tgz#ffc27013038607cdba3288e692c3611c06a18aa4" + integrity sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ== + dependencies: + "@babel/template" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.1", "@babel/parser@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.2.tgz#577518bedb17a2ce4212afd052e01f7df0941127" + integrity sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw== + dependencies: + "@babel/types" "^7.27.1" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/template@^7.27.1", "@babel/template@^7.3.3": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" + +"@babel/traverse@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" + integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.27.1" + "@babel/parser" "^7.27.1" + "@babel/template" "^7.27.1" + "@babel/types" "^7.27.1" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.3.3": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" + integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cloudflare/cabidela@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@cloudflare/cabidela/-/cabidela-0.2.4.tgz#9a3e9212e636a24d796a8f16741c24885b326a1a" + integrity sha512-u/1OwwqfcMvjmUFOcb6QtFzVVGpncHJxwl254wjzp0JC5CUlBkV6r5BbRrHI5ZYJEAgu8NeeorirxngmMFPZjQ== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" + integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@inquirer/checkbox@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-3.0.1.tgz#0a57f704265f78c36e17f07e421b98efb4b9867b" + integrity sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/confirm@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-4.0.1.tgz#9106d6bffa0b2fdd0e4f60319b6f04f2e06e6e25" + integrity sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/core@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.2.1.tgz#677c49dee399c9063f31e0c93f0f37bddc67add1" + integrity sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg== + dependencies: + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + "@types/mute-stream" "^0.0.4" + "@types/node" "^22.5.5" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/editor@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-3.0.1.tgz#d109f21e050af6b960725388cb1c04214ed7c7bc" + integrity sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + external-editor "^3.1.0" + +"@inquirer/expand@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-3.0.1.tgz#aed9183cac4d12811be47a4a895ea8e82a17e22c" + integrity sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/figures@^1.0.6": + version "1.0.13" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.13.tgz#ad0afd62baab1c23175115a9b62f511b6a751e45" + integrity sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw== + +"@inquirer/input@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-3.0.1.tgz#de63d49e516487388508d42049deb70f2cb5f28e" + integrity sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/number@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-2.0.1.tgz#b9863080d02ab7dc2e56e16433d83abea0f2a980" + integrity sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + +"@inquirer/password@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-3.0.1.tgz#2a9a9143591088336bbd573bcb05d5bf080dbf87" + integrity sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + +"@inquirer/prompts@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-6.0.1.tgz#43f5c0ed35c5ebfe52f1d43d46da2d363d950071" + integrity sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A== + dependencies: + "@inquirer/checkbox" "^3.0.1" + "@inquirer/confirm" "^4.0.1" + "@inquirer/editor" "^3.0.1" + "@inquirer/expand" "^3.0.1" + "@inquirer/input" "^3.0.1" + "@inquirer/number" "^2.0.1" + "@inquirer/password" "^3.0.1" + "@inquirer/rawlist" "^3.0.1" + "@inquirer/search" "^2.0.1" + "@inquirer/select" "^3.0.1" + +"@inquirer/rawlist@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-3.0.1.tgz#729def358419cc929045f264131878ed379e0af3" + integrity sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/search@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-2.0.1.tgz#69b774a0a826de2e27b48981d01bc5ad81e73721" + integrity sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/select@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-3.0.1.tgz#1df9ed27fb85a5f526d559ac5ce7cc4e9dc4e7ec" + integrity sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q== + dependencies: + "@inquirer/core" "^9.2.1" + "@inquirer/figures" "^1.0.6" + "@inquirer/type" "^2.0.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/type@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-2.0.0.tgz#08fa513dca2cb6264fe1b0a2fabade051444e3f6" + integrity sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag== + dependencies: + mute-stream "^1.0.0" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@modelcontextprotocol/sdk@^1.11.5": + version "1.17.3" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz#cf92354220f0183d28179e96a9bf3a8f6d3211ae" + integrity sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg== + dependencies: + ajv "^6.12.6" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.0.1" + express-rate-limit "^7.5.0" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.23.8" + zod-to-json-schema "^3.24.1" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgr/core@^0.2.3": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" + integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@ts-morph/common@~0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.20.0.tgz#3f161996b085ba4519731e4d24c35f6cba5b80af" + integrity sha512-7uKjByfbPpwuzkstL3L5MQyuXPSKdoNG93Fmi2JoDcTf3pEP731JdRFAduRVkOs8oqxPsXKA+ScrWkdQ8t/I+Q== + dependencies: + fast-glob "^3.2.12" + minimatch "^7.4.3" + mkdirp "^2.1.6" + path-browserify "^1.0.1" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.7.tgz#968cdc2366ec3da159f61166428ee40f370e56c2" + integrity sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng== + dependencies: + "@babel/types" "^7.20.7" + +"@types/body-parser@*": + version "1.19.6" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.19": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@^5.0.0": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz#2fa94879c9d46b11a5df4c74ac75befd6b283de6" + integrity sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.3.tgz#6c4bc6acddc2e2a587142e1d8be0bce20757e956" + integrity sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.4.0": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/mute-stream@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" + integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "22.15.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.17.tgz#355ccec95f705b664e4332bb64a7f07db30b7055" + integrity sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw== + dependencies: + undici-types "~6.21.0" + +"@types/node@^22.5.5": + version "22.18.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.0.tgz#9e4709be4f104e3568f7dd1c71e2949bf147a47b" + integrity sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ== + dependencies: + undici-types "~6.21.0" + +"@types/qs@*", "@types/qs@^6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/send@*": + version "0.17.5" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.5.tgz#d991d4f2b16f2b1ef497131f00a9114290791e74" + integrity sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.8" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.8.tgz#8180c3fbe4a70e8f00b9f70b9ba7f08f35987877" + integrity sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz#62f1befe59647524994e89de4516d8dcba7a850a" + integrity sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/type-utils" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/parser@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.1.tgz#e9b0ccf30d37dde724ee4d15f4dbc195995cce1b" + integrity sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q== + dependencies: + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz#1eb52e76878f545e4add142e0d8e3e97e7aa443b" + integrity sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + +"@typescript-eslint/type-utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz#be0f438fb24b03568e282a0aed85f776409f970c" + integrity sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA== + dependencies: + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + debug "^4.3.4" + ts-api-utils "^2.0.1" + +"@typescript-eslint/types@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.1.tgz#478ed6f7e8aee1be7b63a60212b6bffe1423b5d4" + integrity sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ== + +"@typescript-eslint/typescript-estree@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz#37792fe7ef4d3021c7580067c8f1ae66daabacdf" + integrity sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.1.tgz#5628ea0393598a0b2f143d0fc6d019f0dee9dd14" + integrity sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + +"@typescript-eslint/visitor-keys@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz#6742b0e3ba1e0c1e35bdaf78c03e759eb8dd8e75" + integrity sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw== + dependencies: + "@typescript-eslint/types" "8.31.1" + eslint-visitor-keys "^4.2.0" + +"@ungap/structured-clone@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@valtown/deno-http-worker@^0.0.21": + version "0.0.21" + resolved "https://registry.yarnpkg.com/@valtown/deno-http-worker/-/deno-http-worker-0.0.21.tgz#9ce3b5c1d0db211fe7ea8297881fe551838474ad" + integrity sha512-16kFuUykann75lNytnXXIQlmpzreZjzdyT27ebT3yNGCS3kKaS1iZYWHc3Si9An54Cphwr4qEcviChQkEeJBlA== + +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1, acorn@^8.9.0: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.12.4, ajv@^6.12.6: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +body-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" + integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.0" + http-errors "^2.0.0" + iconv-lite "^0.6.3" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.0" + type-is "^2.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.24.0: + version "4.24.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.5.tgz#aa0f5b8560fe81fde84c6dcb38f759bafba0e11b" + integrity sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw== + dependencies: + caniuse-lite "^1.0.30001716" + electron-to-chromium "^1.5.149" + node-releases "^2.0.19" + update-browserslist-db "^1.1.3" + +bs-logger@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2, bytes@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001716: + version "1.0.30001717" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz#5d9fec5ce09796a1893013825510678928aca129" + integrity sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw== + +chalk@^4.0.0, chalk@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" + integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +code-block-writer@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770" + integrity sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" + integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7, debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +dedent@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" + integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +depd@2.0.0, depd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.5.149: + version "1.5.151" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz#5edd6c17e1b2f14b4662c41b9379f96cc8c2bb7c" + integrity sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-prettier@^5.0.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz#54d4748904e58eaf1ffe26c4bffa4986ca7f952b" + integrity sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.11.0" + +eslint-plugin-unused-imports@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz#63a98c9ad5f622cd9f830f70bc77739f25ccfe0d" + integrity sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^8.49.0: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventsource-parser@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.3.tgz#e9af1d40b77e6268cdcbc767321e8b9f066adea8" + integrity sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA== + +eventsource-parser@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.1.tgz#5e358dba9a55ba64ca90da883c4ca35bd82467bd" + integrity sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +express-rate-limit@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.0.tgz#6a67990a724b4fbbc69119419feef50c51e8b28f" + integrity sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg== + +express@^5.0.1, express@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" + integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.0" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + +external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.12, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + 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.8" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" + integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +flora-colossus@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a" + integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA== + dependencies: + debug "^4.3.4" + fs-extra "^10.1.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +galactus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e" + integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ== + dependencies: + debug "^4.3.4" + flora-colossus "^2.0.0" + fs-extra "^10.1.0" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-errors@2.0.0, http-errors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.6.3, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.4.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + +"jq-web@https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz": + version "0.8.6" + resolved "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz#14d0e126987736e82e964d675c3838b5944faa6f" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1, make-error@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^3.0.0, mime-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== + dependencies: + mime-db "^1.54.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^7.4.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" + integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-all@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb" + integrity sha512-qUZbvbBFVXm6uJ7U/WDiO0fv6waBMbjlCm4E66oZdRR+egswICarIdHyVSZZHudH8T5SF8x/JG0q0duFzPnlBw== + dependencies: + p-map "^4.0.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" + integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== + +pkce-challenge@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.0.tgz#c3a405cb49e272094a38e890a2b51da0228c4d97" + integrity sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^3.0.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +proxy-addr@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +qs@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" + integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.6.3" + unpipe "1.0.0" + +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve.exports@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== + +resolve@^1.20.0: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +send@^1.1.0, send@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" + integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== + dependencies: + debug "^4.3.5" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.0" + mime-types "^3.0.1" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.1" + +serve-static@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" + integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +statuses@2.0.1, statuses@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-to-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/string-to-stream/-/string-to-stream-3.0.1.tgz#480e6fb4d5476d31cb2221f75307a5dcb6638a42" + integrity sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg== + dependencies: + readable-stream "^3.4.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +superstruct@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" + integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +synckit@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.4.tgz#48972326b59723fc15b8d159803cf8302b545d59" + integrity sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ== + dependencies: + "@pkgr/core" "^0.2.3" + tslib "^2.8.1" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +ts-api-utils@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +ts-jest@^29.1.0: + version "29.3.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.2.tgz#0576cdf0a507f811fe73dcd16d135ce89f8156cb" + integrity sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.1" + type-fest "^4.39.1" + yargs-parser "^21.1.1" + +ts-morph@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-19.0.0.tgz#43e95fb0156c3fe3c77c814ac26b7d0be2f93169" + integrity sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ== + dependencies: + "@ts-morph/common" "~0.20.0" + code-block-writer "^12.0.0" + +ts-node@^10.5.0: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": + version "1.1.9" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" + dependencies: + debug "^4.3.7" + fast-glob "^3.3.2" + get-stdin "^8.0.0" + p-all "^3.0.0" + picocolors "^1.1.1" + signal-exit "^3.0.7" + string-to-stream "^3.0.1" + superstruct "^1.0.4" + tslib "^2.8.1" + yargs "^17.7.2" + +tsconfig-paths@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^4.39.1: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + +type-is@^2.0.0, type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + +typescript@5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + +vary@^1, vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.1, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yoctocolors-cjs@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== + +zod-to-json-schema@^3.24.1, zod-to-json-schema@^3.24.5: + version "3.24.5" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3" + integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== + +zod-validation-error@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.1.tgz#a105723eb40299578a6a38cb86647068f6d005b1" + integrity sha512-F3rdaCOHs5ViJ5YTz5zzRtfkQdMdIeKudJAoxy7yB/2ZMEHw73lmCAcQw11r7++20MyGl4WV59EVh7A9rNAyog== + +zod@^3.23.8: + version "3.24.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f" + integrity sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg== + +zod@^3.25.20, zod@^3.25.67: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..b1909804 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,73 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "node", + "extra-files": [ + "src/version.ts", + "README.md", + "packages/mcp-server/yarn.lock", + { + "type": "json", + "path": "packages/mcp-server/package.json", + "jsonpath": "$.version" + } + ] +} diff --git a/sample/README.md b/sample/README.md deleted file mode 100644 index bb0fdc28..00000000 --- a/sample/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Running the sample application - -### Step 1: Install dependencies -```bash -npm install -``` - -### Step 2: Enter account details -Open `index.js` and fill the account details: -```js -const CONFIG_OPTIONS = { - publicKey: "your_public_api_key", - privateKey: "your_private_api_key", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/", -}; -``` - -### Step 3: -Run the `index.js` \ No newline at end of file diff --git a/sample/index.js b/sample/index.js deleted file mode 100644 index 032f2d96..00000000 --- a/sample/index.js +++ /dev/null @@ -1,292 +0,0 @@ -const ImageKit = require("imagekit"); -const fs = require("fs"); -const path = require("path"); - -const CONFIG_OPTIONS = { - publicKey: "your_public_api_key", - privateKey: "your_private_api_key", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/", -}; - -const FILE_PATH = path.resolve(__dirname, "./test_image.jpg"), - FILE_NAME = "test_image", - IMG_URL = - "https://images.pexels.com/photos/247676/pexels-photo-247676.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260"; - -const sampleApp = async () => { - try { - const imagekit = new ImageKit(CONFIG_OPTIONS); - - // Uploading images through binary - let i = 0; - while (i < 8) { - const response = await uploadLocalFile(imagekit, FILE_PATH, `${FILE_NAME}_bin_${i + 1}`); - console.log(`Binary upload response # ${i + 1}:`, JSON.stringify(response, undefined, 2), "\n"); - i++; - } - - // Uploading images with base64 - const uploadResponse_base64 = await uploadFileBase64(imagekit, FILE_PATH, `${FILE_NAME}_base64`); - console.log(`Base64 upload response:`, JSON.stringify(uploadResponse_base64, undefined, 2), "\n"); - - // Uploading images with buffer - const uploadResponse_buffer = await uploadFileBuffer(imagekit, FILE_PATH, `${FILE_NAME}_buffer`); - console.log(`Buffer upload response:`, JSON.stringify(uploadResponse_buffer, undefined, 2), "\n"); - - // Uploading images with URL - const uploadResponse_url = await uploadFileURL(imagekit, IMG_URL, `${FILE_NAME}_url`); - console.log(`URL upload response:`, JSON.stringify(uploadResponse_url, undefined, 2), "\n"); - - // Listing Files - const filesList = await listFiles(imagekit, 12, 0); - console.log("List of first 10 files: ", JSON.stringify(filesList, undefined, 2), "\n"); - - // Generating URLs - const imageURL = imagekit.url({ - path: filesList[0].filePath, - transformation: [ - { - height: 300, - width: 400, - }, - ], - }); - console.log("Url for first image transformed with height: 300, width: 400: ", imageURL, "\n"); - - var signedUrl = imagekit.url({ - path: filesList[0].filePath, - signed: true, - transformation: [ - { - height: 300, - width: 400, - }, - ], - }); - console.log("Signed Url for first image transformed with height: 300, width: 400: ", signedUrl, "\n"); - - // Get File Details - const fileDetails_1 = await getFileDetails(imagekit, filesList[0].fileId); - console.log("File Details fetched: ", JSON.stringify(fileDetails_1, undefined, 2), "\n"); - - // Get File Metadata - const fileMetadata_1 = await getFileMetadata(imagekit, filesList[0].fileId); - const fileMetadata_2 = await getFileMetadata(imagekit, filesList[1].fileId); - console.log("File metadata fetched: ", JSON.stringify(fileMetadata_1, undefined, 2), "\n"); - - // Update File Details - const fileUpdateResponse = await updateFileDetails( - imagekit, - filesList[0].fileId, - ["buildings", "day"], - "10,10,100,100", - //Uncomment to send extensions parameter - // [ - // { - // name: "google-auto-tagging", - // maxTags: 5, - // minConfidence: 95 - // } - // ] - ); - console.log("File Update Response: ", JSON.stringify(fileUpdateResponse, undefined, 2), "\n"); - - // pHash Distance - console.log(fileMetadata_1.pHash, fileMetadata_2.pHash); - const pHashDistance = imagekit.pHashDistance(fileMetadata_1.pHash, fileMetadata_2.pHash); - console.log(`pHash distance: ${pHashDistance}`, "\n"); - - // purge Cache and purgeCache status - const purgeCacheResponse = await purgeCache(imagekit, filesList[0].url); - console.log("Purge Cache Response: ", JSON.stringify(purgeCacheResponse, undefined, 2), "\n"); - - const purgeStatus = await getPurgeCacheStatus(imagekit, purgeCacheResponse.requestId); - console.log("Purge Response: ", JSON.stringify(purgeStatus, undefined, 2), "\n"); - - // Bulk add tags - let fileIds = filesList.map((file) => file.fileId); - fileIds.shift(); - var tags = ["red", "blue"]; - const bulkAddTagsResponse = await bulkAddTags(imagekit, fileIds, tags); - console.log("Bulk add tags response: ", bulkAddTagsResponse, "\n"); - - // Bulk remove tags - const bulkRemoveTagsResponse = await bulkRemoveTags(imagekit, fileIds, tags); - console.log("Bulk remove tags response: ", bulkRemoveTagsResponse, "\n"); - - // Create folder - const createFolderResponse_1 = await createFolder(imagekit, "folder1", "/"); - const createFolderResponse_2 = await createFolder(imagekit, "folder2", "/"); - console.log("Folder creation response: ", createFolderResponse_2, "\n"); - - // Copy file - const copyFileResponse = await copyFile(imagekit, fileDetails_1.filePath, "/folder1/"); - console.log("File copy response: ", copyFileResponse, "\n"); - - // Move file - const moveFileResponse = await moveFile(imagekit, `/folder1/${fileDetails_1.name}`, "/folder2/"); - console.log("File move response: ", moveFileResponse, "\n"); - - // Copy folder - const copyFolderResponse = await copyFolder(imagekit, "/folder2", "/folder1/"); - console.log("Copy folder response: ", JSON.stringify(copyFolderResponse, undefined, 2), "\n"); - - // Move folder - const moveFolderResponse = await moveFolder(imagekit, "/folder1", "/folder2/"); - console.log("Move folder response: ", JSON.stringify(moveFolderResponse, undefined, 2), "\n"); - - // Get bulk job status - const getBulkJobStatusResponse = await getBulkJobStatus(imagekit, moveFolderResponse.jobId); - console.log("Bulk job status response: ", JSON.stringify(getBulkJobStatusResponse), "\n"); - - // Delete folder - const deleteFolderResponse = await deleteFolder(imagekit, "/folder2/"); - console.log("Delete folder response: ", deleteFolderResponse, "\n"); - - // Deleting Files - const deleteResponse = await deleteFile(imagekit, fileDetails_1.fileId); - console.log("Deletion response: ", deleteResponse, "\n"); - - // Bulk Delete Files - const bulkDeleteResponse = await bulkDeleteFiles(imagekit, fileIds); - console.log("Bulk deletion response: ", bulkDeleteResponse, "\n"); - - //Authentication token - const authenticationParameters = imagekit.getAuthenticationParameters("your_token"); - console.log("Authentication Parameters: ", JSON.stringify(authenticationParameters, undefined, 2), "\n"); - - process.exit(0); - } catch (err) { - console.log("Encounterted Error: ", JSON.stringify(err, undefined, 2)); - process.exit(1); - } -}; - -const uploadLocalFile = async (imagekitInstance, filePath, fileName) => { - const file = fs.createReadStream(filePath); - const response = await imagekitInstance.upload({ file, fileName }); - return response; -}; - -const uploadFileBuffer = async (imagekitInstance, filePath, fileName) => { - const buffer = fs.readFileSync(filePath); - const response = await imagekitInstance.upload({ file: buffer, fileName }); - return response; -}; - -const uploadFileBase64 = async (imagekitInstance, filePath, fileName) => { - const file_base64 = fs.readFileSync(filePath, "base64"); - //Uncomment to send extensions parameter - // var extensions = [ - // { - // name: "google-auto-tagging", - // maxTags: 5, - // minConfidence: 95 - // } - // ]; - const response = await imagekitInstance.upload({ file: file_base64, fileName/*, extensions*/}); - return response; -}; - -const uploadFileURL = async (imagekitInstance, url, fileName) => { - const response = await imagekitInstance.upload({ file: url, fileName }); - return response; -}; - -const listFiles = async (imagekitInstance, limit = 10, skip = 0) => { - const response = await imagekitInstance.listFiles({ - limit, - skip, - }); - return response; -}; - -const getFileDetails = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.getFileDetails(fileId); - return response; -}; - -const getFileMetadata = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.getFileMetadata(fileId); - return response; -}; - -const updateFileDetails = async (imagekitInstance, fileId, tags = [], customCoordinates = "", extensions = [], webhookUrl = "") => { - let options = {}; - if (Array.isArray(tags) && tags.length > 0) Object.assign(options, { tags }); - if (typeof customCoordinates === "string" && customCoordinates.length > 0) - Object.assign(options, { customCoordinates }); - if (Array.isArray(extensions) && extensions.length > 0) - Object.assign(options,{ extensions }); - if (typeof webhookUrl === "string" && webhookUrl.length > 0) - Object.assign(options,{ webhookUrl }) - const response = await imagekitInstance.updateFileDetails(fileId, options); - return response; -}; - -const purgeCache = async (imagekitInstance, url) => { - const response = await imagekitInstance.purgeCache(url); - return response; -}; - -const getPurgeCacheStatus = async (imagekitInstance, requestId) => { - const response = await imagekitInstance.getPurgeCacheStatus(requestId); - return response; -}; - -const deleteFile = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.deleteFile(fileId); - return "success"; -}; - -const bulkDeleteFiles = async (imagekitInstance, fileIds) => { - const response = await imagekitInstance.bulkDeleteFiles(fileIds); - return "success"; -}; - -const bulkAddTags = async (imagekitInstance, fileIds, tags) => { - const response = await imagekitInstance.bulkAddTags(fileIds, tags); - return "success"; -}; - -const bulkRemoveTags = async (imagekitInstance, fileIds, tags) => { - const response = await imagekitInstance.bulkRemoveTags(fileIds, tags); - return "success"; -}; - -const copyFile = async (imagekitInstance, sourceFilePath, destinationPath) => { - const response = await imagekitInstance.copyFile(sourceFilePath, destinationPath); - return "success"; -}; - -const moveFile = async (imagekitInstance, sourceFilePath, destinationPath) => { - const response = await imagekitInstance.moveFile(sourceFilePath, destinationPath); - return "success"; -}; - -const copyFolder = async (imagekitInstance, sourceFolderPath, destinationPath) => { - const response = await imagekitInstance.copyFolder(sourceFolderPath, destinationPath); - return response; -}; - -const moveFolder = async (imagekitInstance, sourceFolderPath, destinationPath) => { - const response = await imagekitInstance.moveFolder(sourceFolderPath, destinationPath); - return response; -}; - -const createFolder = async (imagekitInstance, folderName, parentFolderPath) => { - const response = await imagekitInstance.createFolder(folderName, parentFolderPath); - return "success"; -}; - -const deleteFolder = async (imagekitInstance, folderPath) => { - const response = await imagekitInstance.deleteFolder(folderPath); - return "success"; -}; - -const getBulkJobStatus = async (imagekitInstance, jobId) => { - const response = await imagekitInstance.getBulkJobStatus(jobId); - return response; -}; - -sampleApp(); diff --git a/sample/package.json b/sample/package.json deleted file mode 100644 index fb22c514..00000000 --- a/sample/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "imagekit": "*" - }, - "author": "", - "license": "ISC" -} \ No newline at end of file diff --git a/sample/test_image.jpg b/sample/test_image.jpg deleted file mode 100644 index 8102e278..00000000 Binary files a/sample/test_image.jpg and /dev/null differ diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 00000000..a8b69ff3 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then + brew bundle check >/dev/null 2>&1 || { + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo + } +fi + +echo "==> Installing Node dependencies…" + +PACKAGE_MANAGER=$(command -v yarn >/dev/null 2>&1 && echo "yarn" || echo "npm") + +$PACKAGE_MANAGER install "$@" diff --git a/scripts/build b/scripts/build new file mode 100755 index 00000000..fe159668 --- /dev/null +++ b/scripts/build @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +cd "$(dirname "$0")/.." + +node scripts/utils/check-version.cjs + +# Build into dist and will publish the package from there, +# so that src/resources/foo.ts becomes /resources/foo.js +# This way importing from `"@imagekit/nodejs/resources/foo"` works +# even with `"moduleResolution": "node"` + +rm -rf dist; mkdir dist +# Copy src to dist/src and build from dist/src into dist, so that +# the source map for index.js.map will refer to ./src/index.ts etc +cp -rp src README.md dist +for file in LICENSE CHANGELOG.md; do + if [ -e "${file}" ]; then cp "${file}" dist; fi +done +if [ -e "bin/cli" ]; then + mkdir -p dist/bin + cp -p "bin/cli" dist/bin/; +fi +if [ -e "bin/migration-config.json" ]; then + mkdir -p dist/bin + cp -p "bin/migration-config.json" dist/bin/; +fi +# this converts the export map paths for the dist directory +# and does a few other minor things +node scripts/utils/make-dist-package-json.cjs > dist/package.json + +# build to .js/.mjs/.d.ts files +./node_modules/.bin/tsc-multi +# we need to patch index.js so that `new module.exports()` works for cjs backwards +# compat. No way to get that from index.ts because it would cause compile errors +# when building .mjs +node scripts/utils/fix-index-exports.cjs +cp tsconfig.dist-src.json dist/src/tsconfig.json + +node scripts/utils/postprocess-files.cjs + +# make sure that nothing crashes when we require the output CJS or +# import the output ESM +(cd dist && node -e 'require("@imagekit/nodejs")') +(cd dist && node -e 'import("@imagekit/nodejs")' --input-type=module) + +if [ -e ./scripts/build-deno ] +then + ./scripts/build-deno +fi +# build all sub-packages +for dir in packages/*; do + if [ -d "$dir" ]; then + (cd "$dir" && yarn install && yarn build) + fi +done diff --git a/scripts/build-all b/scripts/build-all new file mode 100755 index 00000000..4e5ac01f --- /dev/null +++ b/scripts/build-all @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# build-all is deprecated, use build instead + +bash ./scripts/build diff --git a/scripts/format b/scripts/format new file mode 100755 index 00000000..7a756401 --- /dev/null +++ b/scripts/format @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running eslint --fix" +./node_modules/.bin/eslint --fix . + +echo "==> Running prettier --write" +# format things eslint didn't +./node_modules/.bin/prettier --write --cache --cache-strategy metadata . '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs' diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 00000000..3ffb78a6 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running eslint" +./node_modules/.bin/eslint . + +echo "==> Building" +./scripts/build + +echo "==> Checking types" +./node_modules/typescript/bin/tsc + +echo "==> Running Are The Types Wrong?" +./node_modules/.bin/attw --pack dist -f json >.attw.json || true +node scripts/utils/attw-report.cjs + +echo "==> Running publint" +./node_modules/.bin/publint dist diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 00000000..0b28f6ea --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" +fi diff --git a/scripts/publish-packages.ts b/scripts/publish-packages.ts new file mode 100644 index 00000000..50e93fef --- /dev/null +++ b/scripts/publish-packages.ts @@ -0,0 +1,102 @@ +/** + * Called from the `create-releases.yml` workflow with the output + * of the release please action as the first argument. + * + * Example JSON input: + * + * ```json + { + "releases_created": "true", + "release_created": "true", + "id": "137967744", + "name": "sdk: v0.14.5", + "tag_name": "sdk-v0.14.5", + "sha": "7cc2ba5c694e76a117f731d4cf0b06f8b8361f2e", + "body": "## 0.14.5 (2024-01-22)\n\n...", + "html_url": "https://github.com/$org/$repo/releases/tag/sdk-v0.14.5", + "draft": "false", + "upload_url": "https://uploads.github.com/repos/$org/$repo/releases/137967744/assets{?name,label}", + "path": ".", + "version": "0.14.5", + "major": "0", + "minor": "14", + "patch": "5", + "packages/additional-sdk--release_created": "true", + "packages/additional-sdk--id": "137967756", + "packages/additional-sdk--name": "additional-sdk: v0.5.2", + "packages/additional-sdk--tag_name": "additional-sdk-v0.5.2", + "packages/additional-sdk--sha": "7cc2ba5c694e76a117f731d4cf0b06f8b8361f2e", + "packages/additional-sdk--body": "## 0.5.2 (2024-01-22)\n\n...", + "packages/additional-sdk--html_url": "https://github.com/$org/$repo/releases/tag/additional-sdk-v0.5.2", + "packages/additional-sdk--draft": "false", + "packages/additional-sdk--upload_url": "https://uploads.github.com/repos/$org/$repo/releases/137967756/assets{?name,label}", + "packages/additional-sdk--path": "packages/additional-sdk", + "packages/additional-sdk--version": "0.5.2", + "packages/additional-sdk--major": "0", + "packages/additional-sdk--minor": "5", + "packages/additional-sdk--patch": "2", + "paths_released": "[\".\",\"packages/additional-sdk\"]" + } + ``` + */ + +import { execSync } from 'child_process'; +import path from 'path'; + +function main() { + const data = process.argv[2] ?? process.env['DATA']; + if (!data) { + throw new Error(`Usage: publish-packages.ts '{"json": "obj"}'`); + } + + const rootDir = path.join(__dirname, '..'); + console.log('root dir', rootDir); + console.log(`publish-packages called with ${data}`); + + const outputs = JSON.parse(data); + + const rawPaths = outputs.paths_released; + + if (!rawPaths) { + console.error(JSON.stringify(outputs, null, 2)); + throw new Error('Expected outputs to contain a truthy `paths_released` property'); + } + if (typeof rawPaths !== 'string') { + console.error(JSON.stringify(outputs, null, 2)); + throw new Error('Expected outputs `paths_released` property to be a JSON string'); + } + + const paths = JSON.parse(rawPaths); + if (!Array.isArray(paths)) { + console.error(JSON.stringify(outputs, null, 2)); + throw new Error('Expected outputs `paths_released` property to be an array'); + } + if (!paths.length) { + console.error(JSON.stringify(outputs, null, 2)); + throw new Error('Expected outputs `paths_released` property to contain at least one entry'); + } + + const publishScriptPath = path.join(rootDir, 'bin', 'publish-npm'); + console.log('Using publish script at', publishScriptPath); + + console.log('Ensuring root package is built'); + console.log(`$ yarn build`); + execSync(`yarn build`, { cwd: rootDir, encoding: 'utf8', stdio: 'inherit' }); + + for (const relPackagePath of paths) { + console.log('\n'); + + const packagePath = path.join(rootDir, relPackagePath); + console.log(`Publishing in directory: ${packagePath}`); + + console.log(`$ yarn install`); + execSync(`yarn install`, { cwd: packagePath, encoding: 'utf8', stdio: 'inherit' }); + + console.log(`$ bash ${publishScriptPath}`); + execSync(`bash ${publishScriptPath}`, { cwd: packagePath, encoding: 'utf8', stdio: 'inherit' }); + } + + console.log('Finished publishing packages'); +} + +main(); diff --git a/scripts/test b/scripts/test new file mode 100755 index 00000000..7bce0516 --- /dev/null +++ b/scripts/test @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +echo "==> Running tests" +./node_modules/.bin/jest "$@" diff --git a/scripts/utils/attw-report.cjs b/scripts/utils/attw-report.cjs new file mode 100644 index 00000000..b3477c0e --- /dev/null +++ b/scripts/utils/attw-report.cjs @@ -0,0 +1,24 @@ +const fs = require('fs'); +const problems = Object.values(JSON.parse(fs.readFileSync('.attw.json', 'utf-8')).problems) + .flat() + .filter( + (problem) => + !( + // This is intentional, if the user specifies .mjs they get ESM. + ( + (problem.kind === 'CJSResolvesToESM' && problem.entrypoint.endsWith('.mjs')) || + // This is intentional for backwards compat reasons. + (problem.kind === 'MissingExportEquals' && problem.implementationFileName.endsWith('/index.js')) || + // this is intentional, we deliberately attempt to import types that may not exist from parent node_modules + // folders to better support various runtimes without triggering automatic type acquisition. + (problem.kind === 'InternalResolutionError' && problem.moduleSpecifier.includes('node_modules')) + ) + ), + ); +fs.unlinkSync('.attw.json'); +if (problems.length) { + process.stdout.write('The types are wrong!\n' + JSON.stringify(problems, null, 2) + '\n'); + process.exitCode = 1; +} else { + process.stdout.write('Types ok!\n'); +} diff --git a/scripts/utils/check-is-in-git-install.sh b/scripts/utils/check-is-in-git-install.sh new file mode 100755 index 00000000..1354eb43 --- /dev/null +++ b/scripts/utils/check-is-in-git-install.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Check if you happen to call prepare for a repository that's already in node_modules. +[ "$(basename "$(dirname "$PWD")")" = 'node_modules' ] || +# The name of the containing directory that 'npm` uses, which looks like +# $HOME/.npm/_cacache/git-cloneXXXXXX +[ "$(basename "$(dirname "$PWD")")" = 'tmp' ] || +# The name of the containing directory that 'yarn` uses, which looks like +# $(yarn cache dir)/.tmp/XXXXX +[ "$(basename "$(dirname "$PWD")")" = '.tmp' ] diff --git a/scripts/utils/check-version.cjs b/scripts/utils/check-version.cjs new file mode 100644 index 00000000..86c56dfd --- /dev/null +++ b/scripts/utils/check-version.cjs @@ -0,0 +1,20 @@ +const fs = require('fs'); +const path = require('path'); + +const main = () => { + const pkg = require('../../package.json'); + const version = pkg['version']; + if (!version) throw 'The version property is not set in the package.json file'; + if (typeof version !== 'string') { + throw `Unexpected type for the package.json version field; got ${typeof version}, expected string`; + } + + const versionFile = path.resolve(__dirname, '..', '..', 'src', 'version.ts'); + const contents = fs.readFileSync(versionFile, 'utf8'); + const output = contents.replace(/(export const VERSION = ')(.*)(')/g, `$1${version}$3`); + fs.writeFileSync(versionFile, output); +}; + +if (require.main === module) { + main(); +} diff --git a/scripts/utils/fix-index-exports.cjs b/scripts/utils/fix-index-exports.cjs new file mode 100644 index 00000000..e5e10b3e --- /dev/null +++ b/scripts/utils/fix-index-exports.cjs @@ -0,0 +1,17 @@ +const fs = require('fs'); +const path = require('path'); + +const indexJs = + process.env['DIST_PATH'] ? + path.resolve(process.env['DIST_PATH'], 'index.js') + : path.resolve(__dirname, '..', '..', 'dist', 'index.js'); + +let before = fs.readFileSync(indexJs, 'utf8'); +let after = before.replace( + /^(\s*Object\.defineProperty\s*\(exports,\s*["']__esModule["'].+)$/m, + `exports = module.exports = function (...args) { + return new exports.default(...args) + } + $1`.replace(/^ /gm, ''), +); +fs.writeFileSync(indexJs, after, 'utf8'); diff --git a/scripts/utils/git-swap.sh b/scripts/utils/git-swap.sh new file mode 100755 index 00000000..79d1888e --- /dev/null +++ b/scripts/utils/git-swap.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -exuo pipefail +# the package is published to NPM from ./dist +# we want the final file structure for git installs to match the npm installs, so we + +# delete everything except ./dist and ./node_modules +find . -maxdepth 1 -mindepth 1 ! -name 'dist' ! -name 'node_modules' -exec rm -rf '{}' + + +# move everything from ./dist to . +mv dist/* . + +# delete the now-empty ./dist +rmdir dist diff --git a/scripts/utils/make-dist-package-json.cjs b/scripts/utils/make-dist-package-json.cjs new file mode 100644 index 00000000..4d6634ea --- /dev/null +++ b/scripts/utils/make-dist-package-json.cjs @@ -0,0 +1,29 @@ +const pkgJson = require(process.env['PKG_JSON_PATH'] || '../../package.json'); + +function processExportMap(m) { + for (const key in m) { + const value = m[key]; + if (typeof value === 'string') m[key] = value.replace(/^\.\/dist\//, './'); + else processExportMap(value); + } +} +processExportMap(pkgJson.exports); + +for (const key of ['types', 'main', 'module']) { + if (typeof pkgJson[key] === 'string') pkgJson[key] = pkgJson[key].replace(/^(\.\/)?dist\//, './'); +} +// Fix bin paths if present +if (pkgJson.bin) { + for (const key in pkgJson.bin) { + if (typeof pkgJson.bin[key] === 'string') { + pkgJson.bin[key] = pkgJson.bin[key].replace(/^(\.\/)?dist\//, './'); + } + } +} + +delete pkgJson.devDependencies; +delete pkgJson.scripts.prepack; +delete pkgJson.scripts.prepublishOnly; +delete pkgJson.scripts.prepare; + +console.log(JSON.stringify(pkgJson, null, 2)); diff --git a/scripts/utils/postprocess-files.cjs b/scripts/utils/postprocess-files.cjs new file mode 100644 index 00000000..deae575e --- /dev/null +++ b/scripts/utils/postprocess-files.cjs @@ -0,0 +1,94 @@ +// @ts-check +const fs = require('fs'); +const path = require('path'); + +const distDir = + process.env['DIST_PATH'] ? + path.resolve(process.env['DIST_PATH']) + : path.resolve(__dirname, '..', '..', 'dist'); + +async function* walk(dir) { + for await (const d of await fs.promises.opendir(dir)) { + const entry = path.join(dir, d.name); + if (d.isDirectory()) yield* walk(entry); + else if (d.isFile()) yield entry; + } +} + +async function postprocess() { + for await (const file of walk(distDir)) { + if (!/(\.d)?[cm]?ts$/.test(file)) continue; + + const code = await fs.promises.readFile(file, 'utf8'); + + // strip out lib="dom", types="node", and types="react" references; these + // are needed at build time, but would pollute the user's TS environment + const transformed = code.replace( + /^ *\/\/\/ * ' '.repeat(match.length - 1) + '\n', + ); + + if (transformed !== code) { + console.error(`wrote ${path.relative(process.cwd(), file)}`); + await fs.promises.writeFile(file, transformed, 'utf8'); + } + } + + const newExports = { + '.': { + require: { + types: './index.d.ts', + default: './index.js', + }, + types: './index.d.mts', + default: './index.mjs', + }, + }; + + for (const entry of await fs.promises.readdir(distDir, { withFileTypes: true })) { + if (entry.isDirectory() && entry.name !== 'src' && entry.name !== 'internal' && entry.name !== 'bin') { + const subpath = './' + entry.name; + newExports[subpath + '/*.mjs'] = { + default: subpath + '/*.mjs', + }; + newExports[subpath + '/*.js'] = { + default: subpath + '/*.js', + }; + newExports[subpath + '/*'] = { + import: subpath + '/*.mjs', + require: subpath + '/*.js', + }; + } else if (entry.isFile() && /\.[cm]?js$/.test(entry.name)) { + const { name, ext } = path.parse(entry.name); + const subpathWithoutExt = './' + name; + const subpath = './' + entry.name; + newExports[subpathWithoutExt] ||= { import: undefined, require: undefined }; + const isModule = ext[1] === 'm'; + if (isModule) { + newExports[subpathWithoutExt].import = subpath; + } else { + newExports[subpathWithoutExt].require = subpath; + } + newExports[subpath] = { + default: subpath, + }; + } + } + await fs.promises.writeFile( + 'dist/package.json', + JSON.stringify( + Object.assign( + /** @type {Record} */ ( + JSON.parse(await fs.promises.readFile('dist/package.json', 'utf-8')) + ), + { + exports: newExports, + }, + ), + null, + 2, + ), + ); +} +postprocess(); diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 00000000..0870aebc --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -exuo pipefail + +RESPONSE=$(curl -X POST "$URL" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(tar "${BASE_PATH:+-C$BASE_PATH}" -cz "${ARTIFACT_PATH:-dist}" | curl -v -X PUT \ + -H "Content-Type: application/gzip" \ + --data-binary @- "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/imagekit-typescript/$SHA'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/api-promise.ts b/src/api-promise.ts new file mode 100644 index 00000000..8c775ee6 --- /dev/null +++ b/src/api-promise.ts @@ -0,0 +1,2 @@ +/** @deprecated Import from ./core/api-promise instead */ +export * from './core/api-promise'; diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 00000000..1560e2bc --- /dev/null +++ b/src/client.ts @@ -0,0 +1,929 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types'; +import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types'; +import { uuid4 } from './internal/utils/uuid'; +import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values'; +import { sleep } from './internal/utils/sleep'; +export type { Logger, LogLevel } from './internal/utils/log'; +import { castToError, isAbortError } from './internal/errors'; +import type { APIResponseProps } from './internal/parse'; +import { getPlatformHeaders } from './internal/detect-platform'; +import * as Shims from './internal/shims'; +import * as Opts from './internal/request-options'; +import { VERSION } from './version'; +import * as Errors from './core/error'; +import * as Uploads from './core/uploads'; +import * as API from './resources/index'; +import { APIPromise } from './core/api-promise'; +import { AssetListParams, AssetListResponse, Assets } from './resources/assets'; +import { + CustomMetadataField, + CustomMetadataFieldCreateParams, + CustomMetadataFieldDeleteResponse, + CustomMetadataFieldListParams, + CustomMetadataFieldListResponse, + CustomMetadataFieldUpdateParams, + CustomMetadataFields, +} from './resources/custom-metadata-fields'; +import { + BaseWebhookEvent, + UnsafeUnwrapWebhookEvent, + UnwrapWebhookEvent, + UploadPostTransformErrorEvent, + UploadPostTransformSuccessEvent, + UploadPreTransformErrorEvent, + UploadPreTransformSuccessEvent, + VideoTransformationAcceptedEvent, + VideoTransformationErrorEvent, + VideoTransformationReadyEvent, + Webhooks, +} from './resources/webhooks'; +import { Accounts } from './resources/accounts/accounts'; +import { Beta } from './resources/beta/beta'; +import { Cache } from './resources/cache/cache'; +import { + File, + FileCopyParams, + FileCopyResponse, + FileMoveParams, + FileMoveResponse, + FileRenameParams, + FileRenameResponse, + FileUpdateParams, + FileUpdateResponse, + FileUploadParams, + FileUploadResponse, + Files, + Folder, + Metadata, + UpdateFileRequest, +} from './resources/files/files'; +import { + FolderCopyParams, + FolderCopyResponse, + FolderCreateParams, + FolderCreateResponse, + FolderDeleteParams, + FolderDeleteResponse, + FolderMoveParams, + FolderMoveResponse, + FolderRenameParams, + FolderRenameResponse, + Folders, +} from './resources/folders/folders'; +import { type Fetch } from './internal/builtin-types'; +import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers'; +import { FinalRequestOptions, RequestOptions } from './internal/request-options'; +import { toBase64 } from './internal/utils/base64'; +import { readEnv } from './internal/utils/env'; +import { + type LogLevel, + type Logger, + formatRequestDetails, + loggerFor, + parseLogLevel, +} from './internal/utils/log'; +import { isEmptyObj } from './internal/utils/values'; + +export interface ClientOptions { + /** + * Your ImageKit private API key (starts with `private_`). + * You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + * + */ + privateKey?: string | undefined; + + /** + * ImageKit uses your API key as username and ignores the password. + * The SDK sets a dummy value. You can ignore this field. + * + */ + password?: string | null | undefined; + + /** + * Your ImageKit webhook secret for verifying webhook signatures (starts with `whsec_`). + * You can find this in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks). + * Only required if you're using webhooks. + * + */ + webhookSecret?: string | null | undefined; + + /** + * Override the default base URL for the API, e.g., "https://api.example.com/v2/" + * + * Defaults to process.env['IMAGE_KIT_BASE_URL']. + */ + baseURL?: string | null | undefined; + + /** + * The maximum amount of time (in milliseconds) that the client should wait for a response + * from the server before timing out a single request. + * + * Note that request timeouts are retried by default, so in a worst-case scenario you may wait + * much longer than this timeout before the promise succeeds or fails. + * + * @unit milliseconds + */ + timeout?: number | undefined; + /** + * Additional `RequestInit` options to be passed to `fetch` calls. + * Properties will be overridden by per-request `fetchOptions`. + */ + fetchOptions?: MergedRequestInit | undefined; + + /** + * Specify a custom `fetch` function implementation. + * + * If not provided, we expect that `fetch` is defined globally. + */ + fetch?: Fetch | undefined; + + /** + * The maximum number of times that the client will retry a request in case of a + * temporary failure, like a network error or a 5XX error from the server. + * + * @default 2 + */ + maxRetries?: number | undefined; + + /** + * Default headers to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * header to `null` in request options. + */ + defaultHeaders?: HeadersLike | undefined; + + /** + * Default query parameters to include with every request to the API. + * + * These can be removed in individual requests by explicitly setting the + * param to `undefined` in request options. + */ + defaultQuery?: Record | undefined; + + /** + * Set the log level. + * + * Defaults to process.env['IMAGE_KIT_LOG'] or 'warn' if it isn't set. + */ + logLevel?: LogLevel | undefined; + + /** + * Set the logger. + * + * Defaults to globalThis.console. + */ + logger?: Logger | undefined; +} + +/** + * API Client for interfacing with the Image Kit API. + */ +export class ImageKit { + privateKey: string; + password: string | null; + webhookSecret: string | null; + + baseURL: string; + maxRetries: number; + timeout: number; + logger: Logger | undefined; + logLevel: LogLevel | undefined; + fetchOptions: MergedRequestInit | undefined; + + private fetch: Fetch; + #encoder: Opts.RequestEncoder; + protected idempotencyHeader?: string; + private _options: ClientOptions; + + /** + * API Client for interfacing with the Image Kit API. + * + * @param {string | undefined} [opts.privateKey=process.env['IMAGEKIT_PRIVATE_KEY'] ?? undefined] + * @param {string | null | undefined} [opts.password=process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'] ?? do_not_set] + * @param {string | null | undefined} [opts.webhookSecret=process.env['IMAGEKIT_WEBHOOK_SECRET'] ?? null] + * @param {string} [opts.baseURL=process.env['IMAGE_KIT_BASE_URL'] ?? https://api.imagekit.io] - Override the default base URL for the API. + * @param {number} [opts.timeout=1 minute] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out. + * @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls. + * @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation. + * @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request. + * @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API. + * @param {Record} opts.defaultQuery - Default query parameters to include with every request to the API. + */ + constructor({ + baseURL = readEnv('IMAGE_KIT_BASE_URL'), + privateKey = readEnv('IMAGEKIT_PRIVATE_KEY'), + password = readEnv('OPTIONAL_IMAGEKIT_IGNORES_THIS') ?? 'do_not_set', + webhookSecret = readEnv('IMAGEKIT_WEBHOOK_SECRET') ?? null, + ...opts + }: ClientOptions = {}) { + if (privateKey === undefined) { + throw new Errors.ImageKitError( + "The IMAGEKIT_PRIVATE_KEY environment variable is missing or empty; either provide it, or instantiate the ImageKit client with an privateKey option, like new ImageKit({ privateKey: 'My Private Key' }).", + ); + } + + const options: ClientOptions = { + privateKey, + password, + webhookSecret, + ...opts, + baseURL: baseURL || `https://api.imagekit.io`, + }; + + this.baseURL = options.baseURL!; + this.timeout = options.timeout ?? ImageKit.DEFAULT_TIMEOUT /* 1 minute */; + this.logger = options.logger ?? console; + const defaultLogLevel = 'warn'; + // Set default logLevel early so that we can log a warning in parseLogLevel. + this.logLevel = defaultLogLevel; + this.logLevel = + parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ?? + parseLogLevel(readEnv('IMAGE_KIT_LOG'), "process.env['IMAGE_KIT_LOG']", this) ?? + defaultLogLevel; + this.fetchOptions = options.fetchOptions; + this.maxRetries = options.maxRetries ?? 2; + this.fetch = options.fetch ?? Shims.getDefaultFetch(); + this.#encoder = Opts.FallbackEncoder; + + this._options = options; + + this.privateKey = privateKey; + this.password = password; + this.webhookSecret = webhookSecret; + } + + /** + * Create a new client instance re-using the same options given to the current client with optional overriding. + */ + withOptions(options: Partial): this { + const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)({ + ...this._options, + baseURL: this.baseURL, + maxRetries: this.maxRetries, + timeout: this.timeout, + logger: this.logger, + logLevel: this.logLevel, + fetch: this.fetch, + fetchOptions: this.fetchOptions, + privateKey: this.privateKey, + password: this.password, + webhookSecret: this.webhookSecret, + ...options, + }); + return client; + } + + /** + * Check whether the base URL is set to its default. + */ + #baseURLOverridden(): boolean { + return this.baseURL !== 'https://api.imagekit.io'; + } + + protected defaultQuery(): Record | undefined { + return this._options.defaultQuery; + } + + protected validateHeaders({ values, nulls }: NullableHeaders) { + if (this.privateKey && this.password && values.get('authorization')) { + return; + } + if (nulls.has('authorization')) { + return; + } + + throw new Error( + 'Could not resolve authentication method. Expected the privateKey or password to be set. Or for the "Authorization" headers to be explicitly omitted', + ); + } + + protected async authHeaders(opts: FinalRequestOptions): Promise { + if (!this.privateKey) { + return undefined; + } + + if (!this.password) { + return undefined; + } + + const credentials = `${this.privateKey}:${this.password}`; + const Authorization = `Basic ${toBase64(credentials)}`; + return buildHeaders([{ Authorization }]); + } + + /** + * Basic re-implementation of `qs.stringify` for primitive types. + */ + protected stringifyQuery(query: Record): string { + return Object.entries(query) + .filter(([_, value]) => typeof value !== 'undefined') + .map(([key, value]) => { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } + if (value === null) { + return `${encodeURIComponent(key)}=`; + } + throw new Errors.ImageKitError( + `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`, + ); + }) + .join('&'); + } + + private getUserAgent(): string { + return `${this.constructor.name}/JS ${VERSION}`; + } + + protected defaultIdempotencyKey(): string { + return `stainless-node-retry-${uuid4()}`; + } + + protected makeStatusError( + status: number, + error: Object, + message: string | undefined, + headers: Headers, + ): Errors.APIError { + return Errors.APIError.generate(status, error, message, headers); + } + + buildURL( + path: string, + query: Record | null | undefined, + defaultBaseURL?: string | undefined, + ): string { + const baseURL = (!this.#baseURLOverridden() && defaultBaseURL) || this.baseURL; + const url = + isAbsoluteURL(path) ? + new URL(path) + : new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path)); + + const defaultQuery = this.defaultQuery(); + if (!isEmptyObj(defaultQuery)) { + query = { ...defaultQuery, ...query }; + } + + if (typeof query === 'object' && query && !Array.isArray(query)) { + url.search = this.stringifyQuery(query as Record); + } + + return url.toString(); + } + + /** + * Used as a callback for mutating the given `FinalRequestOptions` object. + */ + protected async prepareOptions(options: FinalRequestOptions): Promise {} + + /** + * Used as a callback for mutating the given `RequestInit` object. + * + * This is useful for cases where you want to add certain headers based off of + * the request properties, e.g. `method` or `url`. + */ + protected async prepareRequest( + request: RequestInit, + { url, options }: { url: string; options: FinalRequestOptions }, + ): Promise {} + + get(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('get', path, opts); + } + + post(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('post', path, opts); + } + + patch(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('patch', path, opts); + } + + put(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('put', path, opts); + } + + delete(path: string, opts?: PromiseOrValue): APIPromise { + return this.methodRequest('delete', path, opts); + } + + private methodRequest( + method: HTTPMethod, + path: string, + opts?: PromiseOrValue, + ): APIPromise { + return this.request( + Promise.resolve(opts).then((opts) => { + return { method, path, ...opts }; + }), + ); + } + + request( + options: PromiseOrValue, + remainingRetries: number | null = null, + ): APIPromise { + return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined)); + } + + private async makeRequest( + optionsInput: PromiseOrValue, + retriesRemaining: number | null, + retryOfRequestLogID: string | undefined, + ): Promise { + const options = await optionsInput; + const maxRetries = options.maxRetries ?? this.maxRetries; + if (retriesRemaining == null) { + retriesRemaining = maxRetries; + } + + await this.prepareOptions(options); + + const { req, url, timeout } = await this.buildRequest(options, { + retryCount: maxRetries - retriesRemaining, + }); + + await this.prepareRequest(req, { url, options }); + + /** Not an API request ID, just for correlating local log entries. */ + const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0'); + const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`; + const startTime = Date.now(); + + loggerFor(this).debug( + `[${requestLogID}] sending request`, + formatRequestDetails({ + retryOfRequestLogID, + method: options.method, + url, + options, + headers: req.headers, + }), + ); + + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + + const controller = new AbortController(); + const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError); + const headersTime = Date.now(); + + if (response instanceof globalThis.Error) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + if (options.signal?.aborted) { + throw new Errors.APIUserAbortError(); + } + // detect native connection timeout errors + // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)" + // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)" + // others do not provide enough information to distinguish timeouts from other connection errors + const isTimeout = + isAbortError(response) || + /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : '')); + if (retriesRemaining) { + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID); + } + loggerFor(this).info( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`, + ); + loggerFor(this).debug( + `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`, + formatRequestDetails({ + retryOfRequestLogID, + url, + durationMs: headersTime - startTime, + message: response.message, + }), + ); + if (isTimeout) { + throw new Errors.APIConnectionTimeoutError(); + } + throw new Errors.APIConnectionError({ cause: response }); + } + + const responseInfo = `[${requestLogID}${retryLogStr}] ${req.method} ${url} ${ + response.ok ? 'succeeded' : 'failed' + } with status ${response.status} in ${headersTime - startTime}ms`; + + if (!response.ok) { + const shouldRetry = await this.shouldRetry(response); + if (retriesRemaining && shouldRetry) { + const retryMessage = `retrying, ${retriesRemaining} attempts remaining`; + + // We don't need the body of this response. + await Shims.CancelReadableStream(response.body); + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + return this.retryRequest( + options, + retriesRemaining, + retryOfRequestLogID ?? requestLogID, + response.headers, + ); + } + + const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`; + + loggerFor(this).info(`${responseInfo} - ${retryMessage}`); + + const errText = await response.text().catch((err: any) => castToError(err).message); + const errJSON = safeJSON(errText); + const errMessage = errJSON ? undefined : errText; + + loggerFor(this).debug( + `[${requestLogID}] response error (${retryMessage})`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + message: errMessage, + durationMs: Date.now() - startTime, + }), + ); + + const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers); + throw err; + } + + loggerFor(this).info(responseInfo); + loggerFor(this).debug( + `[${requestLogID}] response start`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + headers: response.headers, + durationMs: headersTime - startTime, + }), + ); + + return { response, options, controller, requestLogID, retryOfRequestLogID, startTime }; + } + + async fetchWithTimeout( + url: RequestInfo, + init: RequestInit | undefined, + ms: number, + controller: AbortController, + ): Promise { + const { signal, method, ...options } = init || {}; + if (signal) signal.addEventListener('abort', () => controller.abort()); + + const timeout = setTimeout(() => controller.abort(), ms); + + const isReadableBody = + ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) || + (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body); + + const fetchOptions: RequestInit = { + signal: controller.signal as any, + ...(isReadableBody ? { duplex: 'half' } : {}), + method: 'GET', + ...options, + }; + if (method) { + // Custom methods like 'patch' need to be uppercased + // See https://github.com/nodejs/undici/issues/2294 + fetchOptions.method = method.toUpperCase(); + } + + try { + // use undefined this binding; fetch errors if bound to something else in browser/cloudflare + return await this.fetch.call(undefined, url, fetchOptions); + } finally { + clearTimeout(timeout); + } + } + + private async shouldRetry(response: Response): Promise { + // Note this is not a standard header. + const shouldRetryHeader = response.headers.get('x-should-retry'); + + // If the server explicitly says whether or not to retry, obey. + if (shouldRetryHeader === 'true') return true; + if (shouldRetryHeader === 'false') return false; + + // Retry on request timeouts. + if (response.status === 408) return true; + + // Retry on lock timeouts. + if (response.status === 409) return true; + + // Retry on rate limits. + if (response.status === 429) return true; + + // Retry internal errors. + if (response.status >= 500) return true; + + return false; + } + + private async retryRequest( + options: FinalRequestOptions, + retriesRemaining: number, + requestLogID: string, + responseHeaders?: Headers | undefined, + ): Promise { + let timeoutMillis: number | undefined; + + // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it. + const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms'); + if (retryAfterMillisHeader) { + const timeoutMs = parseFloat(retryAfterMillisHeader); + if (!Number.isNaN(timeoutMs)) { + timeoutMillis = timeoutMs; + } + } + + // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + const retryAfterHeader = responseHeaders?.get('retry-after'); + if (retryAfterHeader && !timeoutMillis) { + const timeoutSeconds = parseFloat(retryAfterHeader); + if (!Number.isNaN(timeoutSeconds)) { + timeoutMillis = timeoutSeconds * 1000; + } else { + timeoutMillis = Date.parse(retryAfterHeader) - Date.now(); + } + } + + // If the API asks us to wait a certain amount of time (and it's a reasonable amount), + // just do what it says, but otherwise calculate a default + if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) { + const maxRetries = options.maxRetries ?? this.maxRetries; + timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); + } + await sleep(timeoutMillis); + + return this.makeRequest(options, retriesRemaining - 1, requestLogID); + } + + private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number { + const initialRetryDelay = 0.5; + const maxRetryDelay = 8.0; + + const numRetries = maxRetries - retriesRemaining; + + // Apply exponential backoff, but not more than the max. + const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay); + + // Apply some jitter, take up to at most 25 percent of the retry time. + const jitter = 1 - Math.random() * 0.25; + + return sleepSeconds * jitter * 1000; + } + + async buildRequest( + inputOptions: FinalRequestOptions, + { retryCount = 0 }: { retryCount?: number } = {}, + ): Promise<{ req: FinalizedRequestInit; url: string; timeout: number }> { + const options = { ...inputOptions }; + const { method, path, query, defaultBaseURL } = options; + + const url = this.buildURL(path!, query as Record, defaultBaseURL); + if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); + options.timeout = options.timeout ?? this.timeout; + const { bodyHeaders, body } = this.buildBody({ options }); + const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount }); + + const req: FinalizedRequestInit = { + method, + headers: reqHeaders, + ...(options.signal && { signal: options.signal }), + ...((globalThis as any).ReadableStream && + body instanceof (globalThis as any).ReadableStream && { duplex: 'half' }), + ...(body && { body }), + ...((this.fetchOptions as any) ?? {}), + ...((options.fetchOptions as any) ?? {}), + }; + + return { req, url, timeout: options.timeout }; + } + + private async buildHeaders({ + options, + method, + bodyHeaders, + retryCount, + }: { + options: FinalRequestOptions; + method: HTTPMethod; + bodyHeaders: HeadersLike; + retryCount: number; + }): Promise { + let idempotencyHeaders: HeadersLike = {}; + if (this.idempotencyHeader && method !== 'get') { + if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey(); + idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey; + } + + const headers = buildHeaders([ + idempotencyHeaders, + { + Accept: 'application/json', + 'User-Agent': this.getUserAgent(), + 'X-Stainless-Retry-Count': String(retryCount), + ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}), + ...getPlatformHeaders(), + }, + await this.authHeaders(options), + this._options.defaultHeaders, + bodyHeaders, + options.headers, + ]); + + this.validateHeaders(headers); + + return headers.values; + } + + private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): { + bodyHeaders: HeadersLike; + body: BodyInit | undefined; + } { + if (!body) { + return { bodyHeaders: undefined, body: undefined }; + } + const headers = buildHeaders([rawHeaders]); + if ( + // Pass raw type verbatim + ArrayBuffer.isView(body) || + body instanceof ArrayBuffer || + body instanceof DataView || + (typeof body === 'string' && + // Preserve legacy string encoding behavior for now + headers.values.has('content-type')) || + // `Blob` is superset of `File` + ((globalThis as any).Blob && body instanceof (globalThis as any).Blob) || + // `FormData` -> `multipart/form-data` + body instanceof FormData || + // `URLSearchParams` -> `application/x-www-form-urlencoded` + body instanceof URLSearchParams || + // Send chunked stream (each chunk has own `length`) + ((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream) + ) { + return { bodyHeaders: undefined, body: body as BodyInit }; + } else if ( + typeof body === 'object' && + (Symbol.asyncIterator in body || + (Symbol.iterator in body && 'next' in body && typeof body.next === 'function')) + ) { + return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable) }; + } else { + return this.#encoder({ body, headers }); + } + } + + static ImageKit = this; + static DEFAULT_TIMEOUT = 60000; // 1 minute + + static ImageKitError = Errors.ImageKitError; + static APIError = Errors.APIError; + static APIConnectionError = Errors.APIConnectionError; + static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError; + static APIUserAbortError = Errors.APIUserAbortError; + static NotFoundError = Errors.NotFoundError; + static ConflictError = Errors.ConflictError; + static RateLimitError = Errors.RateLimitError; + static BadRequestError = Errors.BadRequestError; + static AuthenticationError = Errors.AuthenticationError; + static InternalServerError = Errors.InternalServerError; + static PermissionDeniedError = Errors.PermissionDeniedError; + static UnprocessableEntityError = Errors.UnprocessableEntityError; + + static toFile = Uploads.toFile; + + customMetadataFields: API.CustomMetadataFields = new API.CustomMetadataFields(this); + files: API.Files = new API.Files(this); + assets: API.Assets = new API.Assets(this); + cache: API.Cache = new API.Cache(this); + folders: API.Folders = new API.Folders(this); + accounts: API.Accounts = new API.Accounts(this); + beta: API.Beta = new API.Beta(this); + webhooks: API.Webhooks = new API.Webhooks(this); + helper: API.Helper = new API.Helper(this); +} + +ImageKit.CustomMetadataFields = CustomMetadataFields; +ImageKit.Files = Files; +ImageKit.Assets = Assets; +ImageKit.Cache = Cache; +ImageKit.Folders = Folders; +ImageKit.Accounts = Accounts; +ImageKit.Beta = Beta; +ImageKit.Webhooks = Webhooks; + +export declare namespace ImageKit { + export type RequestOptions = Opts.RequestOptions; + + export { + CustomMetadataFields as CustomMetadataFields, + type CustomMetadataField as CustomMetadataField, + type CustomMetadataFieldListResponse as CustomMetadataFieldListResponse, + type CustomMetadataFieldDeleteResponse as CustomMetadataFieldDeleteResponse, + type CustomMetadataFieldCreateParams as CustomMetadataFieldCreateParams, + type CustomMetadataFieldUpdateParams as CustomMetadataFieldUpdateParams, + type CustomMetadataFieldListParams as CustomMetadataFieldListParams, + }; + + export { + Files as Files, + type File as File, + type Folder as Folder, + type Metadata as Metadata, + type UpdateFileRequest as UpdateFileRequest, + type FileUpdateResponse as FileUpdateResponse, + type FileCopyResponse as FileCopyResponse, + type FileMoveResponse as FileMoveResponse, + type FileRenameResponse as FileRenameResponse, + type FileUploadResponse as FileUploadResponse, + type FileUpdateParams as FileUpdateParams, + type FileCopyParams as FileCopyParams, + type FileMoveParams as FileMoveParams, + type FileRenameParams as FileRenameParams, + type FileUploadParams as FileUploadParams, + }; + + export { + Assets as Assets, + type AssetListResponse as AssetListResponse, + type AssetListParams as AssetListParams, + }; + + export { Cache as Cache }; + + export { + Folders as Folders, + type FolderCreateResponse as FolderCreateResponse, + type FolderDeleteResponse as FolderDeleteResponse, + type FolderCopyResponse as FolderCopyResponse, + type FolderMoveResponse as FolderMoveResponse, + type FolderRenameResponse as FolderRenameResponse, + type FolderCreateParams as FolderCreateParams, + type FolderDeleteParams as FolderDeleteParams, + type FolderCopyParams as FolderCopyParams, + type FolderMoveParams as FolderMoveParams, + type FolderRenameParams as FolderRenameParams, + }; + + export { Accounts as Accounts }; + + export { Beta as Beta }; + + export { + Webhooks as Webhooks, + type BaseWebhookEvent as BaseWebhookEvent, + type UploadPostTransformErrorEvent as UploadPostTransformErrorEvent, + type UploadPostTransformSuccessEvent as UploadPostTransformSuccessEvent, + type UploadPreTransformErrorEvent as UploadPreTransformErrorEvent, + type UploadPreTransformSuccessEvent as UploadPreTransformSuccessEvent, + type VideoTransformationAcceptedEvent as VideoTransformationAcceptedEvent, + type VideoTransformationErrorEvent as VideoTransformationErrorEvent, + type VideoTransformationReadyEvent as VideoTransformationReadyEvent, + type UnsafeUnwrapWebhookEvent as UnsafeUnwrapWebhookEvent, + type UnwrapWebhookEvent as UnwrapWebhookEvent, + }; + + export type BaseOverlay = API.BaseOverlay; + export type Extensions = API.Extensions; + export type ImageOverlay = API.ImageOverlay; + export type Overlay = API.Overlay; + export type OverlayPosition = API.OverlayPosition; + export type OverlayTiming = API.OverlayTiming; + export type SolidColorOverlay = API.SolidColorOverlay; + export type SolidColorOverlayTransformation = API.SolidColorOverlayTransformation; + export type SrcOptions = API.SrcOptions; + export type StreamingResolution = API.StreamingResolution; + export type SubtitleOverlay = API.SubtitleOverlay; + export type SubtitleOverlayTransformation = API.SubtitleOverlayTransformation; + export type TextOverlay = API.TextOverlay; + export type TextOverlayTransformation = API.TextOverlayTransformation; + export type Transformation = API.Transformation; + export type TransformationPosition = API.TransformationPosition; + export type VideoOverlay = API.VideoOverlay; +} diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 00000000..485fce86 --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,3 @@ +# `core` + +This directory holds public modules implementing non-resource-specific SDK functionality. diff --git a/src/core/api-promise.ts b/src/core/api-promise.ts new file mode 100644 index 00000000..53821f14 --- /dev/null +++ b/src/core/api-promise.ts @@ -0,0 +1,92 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { type ImageKit } from '../client'; + +import { type PromiseOrValue } from '../internal/types'; +import { APIResponseProps, defaultParseResponse } from '../internal/parse'; + +/** + * A subclass of `Promise` providing additional helper methods + * for interacting with the SDK. + */ +export class APIPromise extends Promise { + private parsedPromise: Promise | undefined; + #client: ImageKit; + + constructor( + client: ImageKit, + private responsePromise: Promise, + private parseResponse: ( + client: ImageKit, + props: APIResponseProps, + ) => PromiseOrValue = defaultParseResponse, + ) { + super((resolve) => { + // this is maybe a bit weird but this has to be a no-op to not implicitly + // parse the response body; instead .then, .catch, .finally are overridden + // to parse the response + resolve(null as any); + }); + this.#client = client; + } + + _thenUnwrap(transform: (data: T, props: APIResponseProps) => U): APIPromise { + return new APIPromise(this.#client, this.responsePromise, async (client, props) => + transform(await this.parseResponse(client, props), props), + ); + } + + /** + * Gets the raw `Response` instance instead of parsing the response + * data. + * + * If you want to parse the response body but still get the `Response` + * instance, you can use {@link withResponse()}. + * + * 👋 Getting the wrong TypeScript type for `Response`? + * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` + * to your `tsconfig.json`. + */ + asResponse(): Promise { + return this.responsePromise.then((p) => p.response); + } + + /** + * Gets the parsed response data and the raw `Response` instance. + * + * If you just want to get the raw `Response` instance without parsing it, + * you can use {@link asResponse()}. + * + * 👋 Getting the wrong TypeScript type for `Response`? + * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]` + * to your `tsconfig.json`. + */ + async withResponse(): Promise<{ data: T; response: Response }> { + const [data, response] = await Promise.all([this.parse(), this.asResponse()]); + return { data, response }; + } + + private parse(): Promise { + if (!this.parsedPromise) { + this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data)); + } + return this.parsedPromise; + } + + override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, + ): Promise { + return this.parse().then(onfulfilled, onrejected); + } + + override catch( + onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null, + ): Promise { + return this.parse().catch(onrejected); + } + + override finally(onfinally?: (() => void) | undefined | null): Promise { + return this.parse().finally(onfinally); + } +} diff --git a/src/core/error.ts b/src/core/error.ts new file mode 100644 index 00000000..47d1cc24 --- /dev/null +++ b/src/core/error.ts @@ -0,0 +1,130 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { castToError } from '../internal/errors'; + +export class ImageKitError extends Error {} + +export class APIError< + TStatus extends number | undefined = number | undefined, + THeaders extends Headers | undefined = Headers | undefined, + TError extends Object | undefined = Object | undefined, +> extends ImageKitError { + /** HTTP status for the response that caused the error */ + readonly status: TStatus; + /** HTTP headers for the response that caused the error */ + readonly headers: THeaders; + /** JSON body of the response that caused the error */ + readonly error: TError; + + constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) { + super(`${APIError.makeMessage(status, error, message)}`); + this.status = status; + this.headers = headers; + this.error = error; + } + + private static makeMessage(status: number | undefined, error: any, message: string | undefined) { + const msg = + error?.message ? + typeof error.message === 'string' ? + error.message + : JSON.stringify(error.message) + : error ? JSON.stringify(error) + : message; + + if (status && msg) { + return `${status} ${msg}`; + } + if (status) { + return `${status} status code (no body)`; + } + if (msg) { + return msg; + } + return '(no status code or body)'; + } + + static generate( + status: number | undefined, + errorResponse: Object | undefined, + message: string | undefined, + headers: Headers | undefined, + ): APIError { + if (!status || !headers) { + return new APIConnectionError({ message, cause: castToError(errorResponse) }); + } + + const error = errorResponse as Record; + + if (status === 400) { + return new BadRequestError(status, error, message, headers); + } + + if (status === 401) { + return new AuthenticationError(status, error, message, headers); + } + + if (status === 403) { + return new PermissionDeniedError(status, error, message, headers); + } + + if (status === 404) { + return new NotFoundError(status, error, message, headers); + } + + if (status === 409) { + return new ConflictError(status, error, message, headers); + } + + if (status === 422) { + return new UnprocessableEntityError(status, error, message, headers); + } + + if (status === 429) { + return new RateLimitError(status, error, message, headers); + } + + if (status >= 500) { + return new InternalServerError(status, error, message, headers); + } + + return new APIError(status, error, message, headers); + } +} + +export class APIUserAbortError extends APIError { + constructor({ message }: { message?: string } = {}) { + super(undefined, undefined, message || 'Request was aborted.', undefined); + } +} + +export class APIConnectionError extends APIError { + constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) { + super(undefined, undefined, message || 'Connection error.', undefined); + // in some environments the 'cause' property is already declared + // @ts-ignore + if (cause) this.cause = cause; + } +} + +export class APIConnectionTimeoutError extends APIConnectionError { + constructor({ message }: { message?: string } = {}) { + super({ message: message ?? 'Request timed out.' }); + } +} + +export class BadRequestError extends APIError<400, Headers> {} + +export class AuthenticationError extends APIError<401, Headers> {} + +export class PermissionDeniedError extends APIError<403, Headers> {} + +export class NotFoundError extends APIError<404, Headers> {} + +export class ConflictError extends APIError<409, Headers> {} + +export class UnprocessableEntityError extends APIError<422, Headers> {} + +export class RateLimitError extends APIError<429, Headers> {} + +export class InternalServerError extends APIError {} diff --git a/src/core/resource.ts b/src/core/resource.ts new file mode 100644 index 00000000..51525101 --- /dev/null +++ b/src/core/resource.ts @@ -0,0 +1,11 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { ImageKit } from '../client'; + +export abstract class APIResource { + protected _client: ImageKit; + + constructor(client: ImageKit) { + this._client = client; + } +} diff --git a/src/core/uploads.ts b/src/core/uploads.ts new file mode 100644 index 00000000..2882ca6d --- /dev/null +++ b/src/core/uploads.ts @@ -0,0 +1,2 @@ +export { type Uploadable } from '../internal/uploads'; +export { toFile, type ToFileInput } from '../internal/to-file'; diff --git a/src/error.ts b/src/error.ts new file mode 100644 index 00000000..fc55f46c --- /dev/null +++ b/src/error.ts @@ -0,0 +1,2 @@ +/** @deprecated Import from ./core/error instead */ +export * from './core/error'; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..71734d88 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { ImageKit as default } from './client'; + +export { type Uploadable, toFile } from './core/uploads'; +export { APIPromise } from './core/api-promise'; +export { ImageKit, type ClientOptions } from './client'; +export { + ImageKitError, + APIError, + APIConnectionError, + APIConnectionTimeoutError, + APIUserAbortError, + NotFoundError, + ConflictError, + RateLimitError, + BadRequestError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, +} from './core/error'; diff --git a/src/internal/README.md b/src/internal/README.md new file mode 100644 index 00000000..3ef5a25b --- /dev/null +++ b/src/internal/README.md @@ -0,0 +1,3 @@ +# `internal` + +The modules in this directory are not importable outside this package and will change between releases. diff --git a/src/internal/builtin-types.ts b/src/internal/builtin-types.ts new file mode 100644 index 00000000..c23d3bde --- /dev/null +++ b/src/internal/builtin-types.ts @@ -0,0 +1,93 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise; + +/** + * An alias to the builtin `RequestInit` type so we can + * easily alias it in import statements if there are name clashes. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit + */ +type _RequestInit = RequestInit; + +/** + * An alias to the builtin `Response` type so we can + * easily alias it in import statements if there are name clashes. + * + * https://developer.mozilla.org/docs/Web/API/Response + */ +type _Response = Response; + +/** + * The type for the first argument to `fetch`. + * + * https://developer.mozilla.org/docs/Web/API/Window/fetch#resource + */ +type _RequestInfo = Request | URL | string; + +/** + * The type for constructing `RequestInit` Headers. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit#setting_headers + */ +type _HeadersInit = RequestInit['headers']; + +/** + * The type for constructing `RequestInit` body. + * + * https://developer.mozilla.org/docs/Web/API/RequestInit#body + */ +type _BodyInit = RequestInit['body']; + +/** + * An alias to the builtin `Array` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Array = Array; + +/** + * An alias to the builtin `Record` type so we can + * easily alias it in import statements if there are name clashes. + */ +type _Record = Record; + +export type { + _Array as Array, + _BodyInit as BodyInit, + _HeadersInit as HeadersInit, + _Record as Record, + _RequestInfo as RequestInfo, + _RequestInit as RequestInit, + _Response as Response, +}; + +/** + * A copy of the builtin `EndingType` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L27941 + */ +type EndingType = 'native' | 'transparent'; + +/** + * A copy of the builtin `BlobPropertyBag` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L154 + * https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#options + */ +export interface BlobPropertyBag { + endings?: EndingType; + type?: string; +} + +/** + * A copy of the builtin `FilePropertyBag` type as it isn't fully supported in certain + * environments and attempting to reference the global version will error. + * + * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L503 + * https://developer.mozilla.org/en-US/docs/Web/API/File/File#options + */ +export interface FilePropertyBag extends BlobPropertyBag { + lastModified?: number; +} diff --git a/src/internal/detect-platform.ts b/src/internal/detect-platform.ts new file mode 100644 index 00000000..e82d95c9 --- /dev/null +++ b/src/internal/detect-platform.ts @@ -0,0 +1,196 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { VERSION } from '../version'; + +export const isRunningInBrowser = () => { + return ( + // @ts-ignore + typeof window !== 'undefined' && + // @ts-ignore + typeof window.document !== 'undefined' && + // @ts-ignore + typeof navigator !== 'undefined' + ); +}; + +type DetectedPlatform = 'deno' | 'node' | 'edge' | 'unknown'; + +/** + * Note this does not detect 'browser'; for that, use getBrowserInfo(). + */ +function getDetectedPlatform(): DetectedPlatform { + if (typeof Deno !== 'undefined' && Deno.build != null) { + return 'deno'; + } + if (typeof EdgeRuntime !== 'undefined') { + return 'edge'; + } + if ( + Object.prototype.toString.call( + typeof (globalThis as any).process !== 'undefined' ? (globalThis as any).process : 0, + ) === '[object process]' + ) { + return 'node'; + } + return 'unknown'; +} + +declare const Deno: any; +declare const EdgeRuntime: any; +type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown'; +type PlatformName = + | 'MacOS' + | 'Linux' + | 'Windows' + | 'FreeBSD' + | 'OpenBSD' + | 'iOS' + | 'Android' + | `Other:${string}` + | 'Unknown'; +type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari'; +type PlatformProperties = { + 'X-Stainless-Lang': 'js'; + 'X-Stainless-Package-Version': string; + 'X-Stainless-OS': PlatformName; + 'X-Stainless-Arch': Arch; + 'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown'; + 'X-Stainless-Runtime-Version': string; +}; +const getPlatformProperties = (): PlatformProperties => { + const detectedPlatform = getDetectedPlatform(); + if (detectedPlatform === 'deno') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': normalizePlatform(Deno.build.os), + 'X-Stainless-Arch': normalizeArch(Deno.build.arch), + 'X-Stainless-Runtime': 'deno', + 'X-Stainless-Runtime-Version': + typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown', + }; + } + if (typeof EdgeRuntime !== 'undefined') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': `other:${EdgeRuntime}`, + 'X-Stainless-Runtime': 'edge', + 'X-Stainless-Runtime-Version': (globalThis as any).process.version, + }; + } + // Check if Node.js + if (detectedPlatform === 'node') { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform ?? 'unknown'), + 'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch ?? 'unknown'), + 'X-Stainless-Runtime': 'node', + 'X-Stainless-Runtime-Version': (globalThis as any).process.version ?? 'unknown', + }; + } + + const browserInfo = getBrowserInfo(); + if (browserInfo) { + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': 'unknown', + 'X-Stainless-Runtime': `browser:${browserInfo.browser}`, + 'X-Stainless-Runtime-Version': browserInfo.version, + }; + } + + // TODO add support for Cloudflare workers, etc. + return { + 'X-Stainless-Lang': 'js', + 'X-Stainless-Package-Version': VERSION, + 'X-Stainless-OS': 'Unknown', + 'X-Stainless-Arch': 'unknown', + 'X-Stainless-Runtime': 'unknown', + 'X-Stainless-Runtime-Version': 'unknown', + }; +}; + +type BrowserInfo = { + browser: Browser; + version: string; +}; + +declare const navigator: { userAgent: string } | undefined; + +// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts +function getBrowserInfo(): BrowserInfo | null { + if (typeof navigator === 'undefined' || !navigator) { + return null; + } + + // NOTE: The order matters here! + const browserPatterns = [ + { key: 'edge' as const, pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'ie' as const, pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'ie' as const, pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'chrome' as const, pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'firefox' as const, pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ }, + { key: 'safari' as const, pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ }, + ]; + + // Find the FIRST matching browser + for (const { key, pattern } of browserPatterns) { + const match = pattern.exec(navigator.userAgent); + if (match) { + const major = match[1] || 0; + const minor = match[2] || 0; + const patch = match[3] || 0; + + return { browser: key, version: `${major}.${minor}.${patch}` }; + } + } + + return null; +} + +const normalizeArch = (arch: string): Arch => { + // Node docs: + // - https://nodejs.org/api/process.html#processarch + // Deno docs: + // - https://doc.deno.land/deno/stable/~/Deno.build + if (arch === 'x32') return 'x32'; + if (arch === 'x86_64' || arch === 'x64') return 'x64'; + if (arch === 'arm') return 'arm'; + if (arch === 'aarch64' || arch === 'arm64') return 'arm64'; + if (arch) return `other:${arch}`; + return 'unknown'; +}; + +const normalizePlatform = (platform: string): PlatformName => { + // Node platforms: + // - https://nodejs.org/api/process.html#processplatform + // Deno platforms: + // - https://doc.deno.land/deno/stable/~/Deno.build + // - https://github.com/denoland/deno/issues/14799 + + platform = platform.toLowerCase(); + + // NOTE: this iOS check is untested and may not work + // Node does not work natively on IOS, there is a fork at + // https://github.com/nodejs-mobile/nodejs-mobile + // however it is unknown at the time of writing how to detect if it is running + if (platform.includes('ios')) return 'iOS'; + if (platform === 'android') return 'Android'; + if (platform === 'darwin') return 'MacOS'; + if (platform === 'win32') return 'Windows'; + if (platform === 'freebsd') return 'FreeBSD'; + if (platform === 'openbsd') return 'OpenBSD'; + if (platform === 'linux') return 'Linux'; + if (platform) return `Other:${platform}`; + return 'Unknown'; +}; + +let _platformHeaders: PlatformProperties; +export const getPlatformHeaders = () => { + return (_platformHeaders ??= getPlatformProperties()); +}; diff --git a/src/internal/errors.ts b/src/internal/errors.ts new file mode 100644 index 00000000..82c7b14d --- /dev/null +++ b/src/internal/errors.ts @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export function isAbortError(err: unknown) { + return ( + typeof err === 'object' && + err !== null && + // Spec-compliant fetch implementations + (('name' in err && (err as any).name === 'AbortError') || + // Expo fetch + ('message' in err && String((err as any).message).includes('FetchRequestCanceledException'))) + ); +} + +export const castToError = (err: any): Error => { + if (err instanceof Error) return err; + if (typeof err === 'object' && err !== null) { + try { + if (Object.prototype.toString.call(err) === '[object Error]') { + // @ts-ignore - not all envs have native support for cause yet + const error = new Error(err.message, err.cause ? { cause: err.cause } : {}); + if (err.stack) error.stack = err.stack; + // @ts-ignore - not all envs have native support for cause yet + if (err.cause && !error.cause) error.cause = err.cause; + if (err.name) error.name = err.name; + return error; + } + } catch {} + try { + return new Error(JSON.stringify(err)); + } catch {} + } + return new Error(err); +}; diff --git a/src/internal/headers.ts b/src/internal/headers.ts new file mode 100644 index 00000000..c724a9d2 --- /dev/null +++ b/src/internal/headers.ts @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { isReadonlyArray } from './utils/values'; + +type HeaderValue = string | undefined | null; +export type HeadersLike = + | Headers + | readonly HeaderValue[][] + | Record + | undefined + | null + | NullableHeaders; + +const brand_privateNullableHeaders = /* @__PURE__ */ Symbol('brand.privateNullableHeaders'); + +/** + * @internal + * Users can pass explicit nulls to unset default headers. When we parse them + * into a standard headers type we need to preserve that information. + */ +export type NullableHeaders = { + /** Brand check, prevent users from creating a NullableHeaders. */ + [brand_privateNullableHeaders]: true; + /** Parsed headers. */ + values: Headers; + /** Set of lowercase header names explicitly set to null. */ + nulls: Set; +}; + +function* iterateHeaders(headers: HeadersLike): IterableIterator { + if (!headers) return; + + if (brand_privateNullableHeaders in headers) { + const { values, nulls } = headers; + yield* values.entries(); + for (const name of nulls) { + yield [name, null]; + } + return; + } + + let shouldClear = false; + let iter: Iterable; + if (headers instanceof Headers) { + iter = headers.entries(); + } else if (isReadonlyArray(headers)) { + iter = headers; + } else { + shouldClear = true; + iter = Object.entries(headers ?? {}); + } + for (let row of iter) { + const name = row[0]; + if (typeof name !== 'string') throw new TypeError('expected header name to be a string'); + const values = isReadonlyArray(row[1]) ? row[1] : [row[1]]; + let didClear = false; + for (const value of values) { + if (value === undefined) continue; + + // Objects keys always overwrite older headers, they never append. + // Yield a null to clear the header before adding the new values. + if (shouldClear && !didClear) { + didClear = true; + yield [name, null]; + } + yield [name, value]; + } + } +} + +export const buildHeaders = (newHeaders: HeadersLike[]): NullableHeaders => { + const targetHeaders = new Headers(); + const nullHeaders = new Set(); + for (const headers of newHeaders) { + const seenHeaders = new Set(); + for (const [name, value] of iterateHeaders(headers)) { + const lowerName = name.toLowerCase(); + if (!seenHeaders.has(lowerName)) { + targetHeaders.delete(name); + seenHeaders.add(lowerName); + } + if (value === null) { + targetHeaders.delete(name); + nullHeaders.add(lowerName); + } else { + targetHeaders.append(name, value); + nullHeaders.delete(lowerName); + } + } + } + return { [brand_privateNullableHeaders]: true, values: targetHeaders, nulls: nullHeaders }; +}; + +export const isEmptyHeaders = (headers: HeadersLike) => { + for (const _ of iterateHeaders(headers)) return false; + return true; +}; diff --git a/src/internal/parse.ts b/src/internal/parse.ts new file mode 100644 index 00000000..10d1ca90 --- /dev/null +++ b/src/internal/parse.ts @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import type { FinalRequestOptions } from './request-options'; +import { type ImageKit } from '../client'; +import { formatRequestDetails, loggerFor } from './utils/log'; + +export type APIResponseProps = { + response: Response; + options: FinalRequestOptions; + controller: AbortController; + requestLogID: string; + retryOfRequestLogID: string | undefined; + startTime: number; +}; + +export async function defaultParseResponse(client: ImageKit, props: APIResponseProps): Promise { + const { response, requestLogID, retryOfRequestLogID, startTime } = props; + const body = await (async () => { + // fetch refuses to read the body when the status code is 204. + if (response.status === 204) { + return null as T; + } + + if (props.options.__binaryResponse) { + return response as unknown as T; + } + + const contentType = response.headers.get('content-type'); + const mediaType = contentType?.split(';')[0]?.trim(); + const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json'); + if (isJSON) { + const json = await response.json(); + return json as T; + } + + const text = await response.text(); + return text as unknown as T; + })(); + loggerFor(client).debug( + `[${requestLogID}] response parsed`, + formatRequestDetails({ + retryOfRequestLogID, + url: response.url, + status: response.status, + body, + durationMs: Date.now() - startTime, + }), + ); + return body; +} diff --git a/src/internal/request-options.ts b/src/internal/request-options.ts new file mode 100644 index 00000000..2aabf9aa --- /dev/null +++ b/src/internal/request-options.ts @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { NullableHeaders } from './headers'; + +import type { BodyInit } from './builtin-types'; +import type { HTTPMethod, MergedRequestInit } from './types'; +import { type HeadersLike } from './headers'; + +export type FinalRequestOptions = RequestOptions & { method: HTTPMethod; path: string }; + +export type RequestOptions = { + /** + * The HTTP method for the request (e.g., 'get', 'post', 'put', 'delete'). + */ + method?: HTTPMethod; + + /** + * The URL path for the request. + * + * @example "/v1/foo" + */ + path?: string; + + /** + * Query parameters to include in the request URL. + */ + query?: object | undefined | null; + + /** + * The request body. Can be a string, JSON object, FormData, or other supported types. + */ + body?: unknown; + + /** + * HTTP headers to include with the request. Can be a Headers object, plain object, or array of tuples. + */ + headers?: HeadersLike; + + /** + * The maximum number of times that the client will retry a request in case of a + * temporary failure, like a network error or a 5XX error from the server. + * + * @default 2 + */ + maxRetries?: number; + + stream?: boolean | undefined; + + /** + * The maximum amount of time (in milliseconds) that the client should wait for a response + * from the server before timing out a single request. + * + * @unit milliseconds + */ + timeout?: number; + + /** + * Additional `RequestInit` options to be passed to the underlying `fetch` call. + * These options will be merged with the client's default fetch options. + */ + fetchOptions?: MergedRequestInit; + + /** + * An AbortSignal that can be used to cancel the request. + */ + signal?: AbortSignal | undefined | null; + + /** + * A unique key for this request to enable idempotency. + */ + idempotencyKey?: string; + + /** + * Override the default base URL for this specific request. + */ + defaultBaseURL?: string | undefined; + + __binaryResponse?: boolean | undefined; +}; + +export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit }; +export type RequestEncoder = (request: { headers: NullableHeaders; body: unknown }) => EncodedContent; + +export const FallbackEncoder: RequestEncoder = ({ headers, body }) => { + return { + bodyHeaders: { + 'content-type': 'application/json', + }, + body: JSON.stringify(body), + }; +}; diff --git a/src/internal/shim-types.ts b/src/internal/shim-types.ts new file mode 100644 index 00000000..8ddf7b0a --- /dev/null +++ b/src/internal/shim-types.ts @@ -0,0 +1,26 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * Shims for types that we can't always rely on being available globally. + * + * Note: these only exist at the type-level, there is no corresponding runtime + * version for any of these symbols. + */ + +type NeverToAny = T extends never ? any : T; + +/** @ts-ignore */ +type _DOMReadableStream = globalThis.ReadableStream; + +/** @ts-ignore */ +type _NodeReadableStream = import('stream/web').ReadableStream; + +type _ConditionalNodeReadableStream = + typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream; + +type _ReadableStream = NeverToAny< + | ([0] extends [1 & _DOMReadableStream] ? never : _DOMReadableStream) + | ([0] extends [1 & _ConditionalNodeReadableStream] ? never : _ConditionalNodeReadableStream) +>; + +export type { _ReadableStream as ReadableStream }; diff --git a/src/internal/shims.ts b/src/internal/shims.ts new file mode 100644 index 00000000..e4dc7102 --- /dev/null +++ b/src/internal/shims.ts @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * This module provides internal shims and utility functions for environments where certain Node.js or global types may not be available. + * + * These are used to ensure we can provide a consistent behaviour between different JavaScript environments and good error + * messages in cases where an environment isn't fully supported. + */ + +import type { Fetch } from './builtin-types'; +import type { ReadableStream } from './shim-types'; + +export function getDefaultFetch(): Fetch { + if (typeof fetch !== 'undefined') { + return fetch as any; + } + + throw new Error( + '`fetch` is not defined as a global; Either pass `fetch` to the client, `new ImageKit({ fetch })` or polyfill the global, `globalThis.fetch = fetch`', + ); +} + +type ReadableStreamArgs = ConstructorParameters; + +export function makeReadableStream(...args: ReadableStreamArgs): ReadableStream { + const ReadableStream = (globalThis as any).ReadableStream; + if (typeof ReadableStream === 'undefined') { + // Note: All of the platforms / runtimes we officially support already define + // `ReadableStream` as a global, so this should only ever be hit on unsupported runtimes. + throw new Error( + '`ReadableStream` is not defined as a global; You will need to polyfill it, `globalThis.ReadableStream = ReadableStream`', + ); + } + + return new ReadableStream(...args); +} + +export function ReadableStreamFrom(iterable: Iterable | AsyncIterable): ReadableStream { + let iter: AsyncIterator | Iterator = + Symbol.asyncIterator in iterable ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator](); + + return makeReadableStream({ + start() {}, + async pull(controller: any) { + const { done, value } = await iter.next(); + if (done) { + controller.close(); + } else { + controller.enqueue(value); + } + }, + async cancel() { + await iter.return?.(); + }, + }); +} + +/** + * Most browsers don't yet have async iterable support for ReadableStream, + * and Node has a very different way of reading bytes from its "ReadableStream". + * + * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490 + */ +export function ReadableStreamToAsyncIterable(stream: any): AsyncIterableIterator { + if (stream[Symbol.asyncIterator]) return stream; + + const reader = stream.getReader(); + return { + async next() { + try { + const result = await reader.read(); + if (result?.done) reader.releaseLock(); // release lock when stream becomes closed + return result; + } catch (e) { + reader.releaseLock(); // release lock when stream becomes errored + throw e; + } + }, + async return() { + const cancelPromise = reader.cancel(); + reader.releaseLock(); + await cancelPromise; + return { done: true, value: undefined }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; +} + +/** + * Cancels a ReadableStream we don't need to consume. + * See https://undici.nodejs.org/#/?id=garbage-collection + */ +export async function CancelReadableStream(stream: any): Promise { + if (stream === null || typeof stream !== 'object') return; + + if (stream[Symbol.asyncIterator]) { + await stream[Symbol.asyncIterator]().return?.(); + return; + } + + const reader = stream.getReader(); + const cancelPromise = reader.cancel(); + reader.releaseLock(); + await cancelPromise; +} diff --git a/src/internal/to-file.ts b/src/internal/to-file.ts new file mode 100644 index 00000000..245e8493 --- /dev/null +++ b/src/internal/to-file.ts @@ -0,0 +1,154 @@ +import { BlobPart, getName, makeFile, isAsyncIterable } from './uploads'; +import type { FilePropertyBag } from './builtin-types'; +import { checkFileSupport } from './uploads'; + +type BlobLikePart = string | ArrayBuffer | ArrayBufferView | BlobLike | DataView; + +/** + * Intended to match DOM Blob, node-fetch Blob, node:buffer Blob, etc. + * Don't add arrayBuffer here, node-fetch doesn't have it + */ +interface BlobLike { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + readonly size: number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + readonly type: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number): BlobLike; +} + +/** + * This check adds the arrayBuffer() method type because it is available and used at runtime + */ +const isBlobLike = (value: any): value is BlobLike & { arrayBuffer(): Promise } => + value != null && + typeof value === 'object' && + typeof value.size === 'number' && + typeof value.type === 'string' && + typeof value.text === 'function' && + typeof value.slice === 'function' && + typeof value.arrayBuffer === 'function'; + +/** + * Intended to match DOM File, node:buffer File, undici File, etc. + */ +interface FileLike extends BlobLike { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + readonly lastModified: number; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + readonly name?: string | undefined; +} + +/** + * This check adds the arrayBuffer() method type because it is available and used at runtime + */ +const isFileLike = (value: any): value is FileLike & { arrayBuffer(): Promise } => + value != null && + typeof value === 'object' && + typeof value.name === 'string' && + typeof value.lastModified === 'number' && + isBlobLike(value); + +/** + * Intended to match DOM Response, node-fetch Response, undici Response, etc. + */ +export interface ResponseLike { + url: string; + blob(): Promise; +} + +const isResponseLike = (value: any): value is ResponseLike => + value != null && + typeof value === 'object' && + typeof value.url === 'string' && + typeof value.blob === 'function'; + +export type ToFileInput = + | FileLike + | ResponseLike + | Exclude + | AsyncIterable; + +/** + * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats + * @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s + * @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible + * @param {Object=} options additional properties + * @param {string=} options.type the MIME type of the content + * @param {number=} options.lastModified the last modified timestamp + * @returns a {@link File} with the given properties + */ +export async function toFile( + value: ToFileInput | PromiseLike, + name?: string | null | undefined, + options?: FilePropertyBag | undefined, +): Promise { + checkFileSupport(); + + // If it's a promise, resolve it. + value = await value; + + // If we've been given a `File` we don't need to do anything + if (isFileLike(value)) { + if (value instanceof File) { + return value; + } + return makeFile([await value.arrayBuffer()], value.name); + } + + if (isResponseLike(value)) { + const blob = await value.blob(); + name ||= new URL(value.url).pathname.split(/[\\/]/).pop(); + + return makeFile(await getBytes(blob), name, options); + } + + const parts = await getBytes(value); + + name ||= getName(value); + + if (!options?.type) { + const type = parts.find((part) => typeof part === 'object' && 'type' in part && part.type); + if (typeof type === 'string') { + options = { ...options, type }; + } + } + + return makeFile(parts, name, options); +} + +async function getBytes(value: BlobLikePart | AsyncIterable): Promise> { + let parts: Array = []; + if ( + typeof value === 'string' || + ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc. + value instanceof ArrayBuffer + ) { + parts.push(value); + } else if (isBlobLike(value)) { + parts.push(value instanceof Blob ? value : await value.arrayBuffer()); + } else if ( + isAsyncIterable(value) // includes Readable, ReadableStream, etc. + ) { + for await (const chunk of value) { + parts.push(...(await getBytes(chunk as BlobLikePart))); // TODO, consider validating? + } + } else { + const constructor = value?.constructor?.name; + throw new Error( + `Unexpected data type: ${typeof value}${ + constructor ? `; constructor: ${constructor}` : '' + }${propsForError(value)}`, + ); + } + + return parts; +} + +function propsForError(value: unknown): string { + if (typeof value !== 'object' || value === null) return ''; + const props = Object.getOwnPropertyNames(value); + return `; props: [${props.map((p) => `"${p}"`).join(', ')}]`; +} diff --git a/src/internal/types.ts b/src/internal/types.ts new file mode 100644 index 00000000..b668dfc0 --- /dev/null +++ b/src/internal/types.ts @@ -0,0 +1,95 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export type PromiseOrValue = T | Promise; +export type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; + +export type KeysEnum = { [P in keyof Required]: true }; + +export type FinalizedRequestInit = RequestInit & { headers: Headers }; + +type NotAny = [0] extends [1 & T] ? never : T; + +/** + * Some environments overload the global fetch function, and Parameters only gets the last signature. + */ +type OverloadedParameters = + T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + (...args: infer C): unknown; + (...args: infer D): unknown; + } + ) ? + A | B | C | D + : T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + (...args: infer C): unknown; + } + ) ? + A | B | C + : T extends ( + { + (...args: infer A): unknown; + (...args: infer B): unknown; + } + ) ? + A | B + : T extends (...args: infer A) => unknown ? A + : never; + +/* eslint-disable */ +/** + * These imports attempt to get types from a parent package's dependencies. + * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which + * would cause typescript to show types not present at runtime. To avoid this, we import + * directly from parent node_modules folders. + * + * We need to check multiple levels because we don't know what directory structure we'll be in. + * For example, pnpm generates directories like this: + * ``` + * node_modules + * ├── .pnpm + * │ └── pkg@1.0.0 + * │ └── node_modules + * │ └── pkg + * │ └── internal + * │ └── types.d.ts + * ├── pkg -> .pnpm/pkg@1.0.0/node_modules/pkg + * └── undici + * ``` + * + * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition + */ +/** @ts-ignore For users with \@types/node */ +type UndiciTypesRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with undici */ +type UndiciRequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with \@types/bun */ +type BunRequestInit = globalThis.FetchRequestInit; +/** @ts-ignore For users with node-fetch@2 */ +type NodeFetch2RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ +type NodeFetch3RequestInit = NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny | NotAny; +/** @ts-ignore For users who use Deno */ +type FetchRequestInit = NonNullable[1]>; +/* eslint-enable */ + +type RequestInits = + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny + | NotAny; + +/** + * This type contains `RequestInit` options that may be available on the current runtime, + * including per-platform extensions like `dispatcher`, `agent`, `client`, etc. + */ +export type MergedRequestInit = RequestInits & + /** We don't include these in the types as they'll be overridden for every request. */ + Partial>; diff --git a/src/internal/uploads.ts b/src/internal/uploads.ts new file mode 100644 index 00000000..bfc86b43 --- /dev/null +++ b/src/internal/uploads.ts @@ -0,0 +1,187 @@ +import { type RequestOptions } from './request-options'; +import type { FilePropertyBag, Fetch } from './builtin-types'; +import type { ImageKit } from '../client'; +import { ReadableStreamFrom } from './shims'; + +export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob | DataView; +type FsReadStream = AsyncIterable & { path: string | { toString(): string } }; + +// https://github.com/oven-sh/bun/issues/5980 +interface BunFile extends Blob { + readonly name?: string | undefined; +} + +export const checkFileSupport = () => { + if (typeof File === 'undefined') { + const { process } = globalThis as any; + const isOldNode = + typeof process?.versions?.node === 'string' && parseInt(process.versions.node.split('.')) < 20; + throw new Error( + '`File` is not defined as a global, which is required for file uploads.' + + (isOldNode ? + " Update to Node 20 LTS or newer, or set `globalThis.File` to `import('node:buffer').File`." + : ''), + ); + } +}; + +/** + * Typically, this is a native "File" class. + * + * We provide the {@link toFile} utility to convert a variety of objects + * into the File class. + * + * For convenience, you can also pass a fetch Response, or in Node, + * the result of fs.createReadStream(). + */ +export type Uploadable = File | Response | FsReadStream | BunFile; + +/** + * Construct a `File` instance. This is used to ensure a helpful error is thrown + * for environments that don't define a global `File` yet. + */ +export function makeFile( + fileBits: BlobPart[], + fileName: string | undefined, + options?: FilePropertyBag, +): File { + checkFileSupport(); + return new File(fileBits as any, fileName ?? 'unknown_file', options); +} + +export function getName(value: any): string | undefined { + return ( + ( + (typeof value === 'object' && + value !== null && + (('name' in value && value.name && String(value.name)) || + ('url' in value && value.url && String(value.url)) || + ('filename' in value && value.filename && String(value.filename)) || + ('path' in value && value.path && String(value.path)))) || + '' + ) + .split(/[\\/]/) + .pop() || undefined + ); +} + +export const isAsyncIterable = (value: any): value is AsyncIterable => + value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function'; + +/** + * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value. + * Otherwise returns the request as is. + */ +export const maybeMultipartFormRequestOptions = async ( + opts: RequestOptions, + fetch: ImageKit | Fetch, +): Promise => { + if (!hasUploadableValue(opts.body)) return opts; + + return { ...opts, body: await createForm(opts.body, fetch) }; +}; + +type MultipartFormRequestOptions = Omit & { body: unknown }; + +export const multipartFormRequestOptions = async ( + opts: MultipartFormRequestOptions, + fetch: ImageKit | Fetch, +): Promise => { + return { ...opts, body: await createForm(opts.body, fetch) }; +}; + +const supportsFormDataMap = /* @__PURE__ */ new WeakMap>(); + +/** + * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending + * properly-encoded form data, it just stringifies the object, resulting in a request body of "[object FormData]". + * This function detects if the fetch function provided supports the global FormData object to avoid + * confusing error messages later on. + */ +function supportsFormData(fetchObject: ImageKit | Fetch): Promise { + const fetch: Fetch = typeof fetchObject === 'function' ? fetchObject : (fetchObject as any).fetch; + const cached = supportsFormDataMap.get(fetch); + if (cached) return cached; + const promise = (async () => { + try { + const FetchResponse = ( + 'Response' in fetch ? + fetch.Response + : (await fetch('data:,')).constructor) as typeof Response; + const data = new FormData(); + if (data.toString() === (await new FetchResponse(data).text())) { + return false; + } + return true; + } catch { + // avoid false negatives + return true; + } + })(); + supportsFormDataMap.set(fetch, promise); + return promise; +} + +export const createForm = async >( + body: T | undefined, + fetch: ImageKit | Fetch, +): Promise => { + if (!(await supportsFormData(fetch))) { + throw new TypeError( + 'The provided fetch function does not support file uploads with the current global FormData class.', + ); + } + const form = new FormData(); + await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value))); + return form; +}; + +// We check for Blob not File because Bun.File doesn't inherit from File, +// but they both inherit from Blob and have a `name` property at runtime. +const isNamedBlob = (value: unknown) => value instanceof Blob && 'name' in value; + +const isUploadable = (value: unknown) => + typeof value === 'object' && + value !== null && + (value instanceof Response || isAsyncIterable(value) || isNamedBlob(value)); + +const hasUploadableValue = (value: unknown): boolean => { + if (isUploadable(value)) return true; + if (Array.isArray(value)) return value.some(hasUploadableValue); + if (value && typeof value === 'object') { + for (const k in value) { + if (hasUploadableValue((value as any)[k])) return true; + } + } + return false; +}; + +const addFormValue = async (form: FormData, key: string, value: unknown): Promise => { + if (value === undefined) return; + if (value == null) { + throw new TypeError( + `Received null for "${key}"; to pass null in FormData, you must use the string 'null'`, + ); + } + + // TODO: make nested formats configurable + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + form.append(key, String(value)); + } else if (value instanceof Response) { + form.append(key, makeFile([await value.blob()], getName(value))); + } else if (isAsyncIterable(value)) { + form.append(key, makeFile([await new Response(ReadableStreamFrom(value)).blob()], getName(value))); + } else if (isNamedBlob(value)) { + form.append(key, value, getName(value)); + } else if (Array.isArray(value)) { + await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry))); + } else if (typeof value === 'object') { + await Promise.all( + Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)), + ); + } else { + throw new TypeError( + `Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`, + ); + } +}; diff --git a/src/internal/utils.ts b/src/internal/utils.ts new file mode 100644 index 00000000..3cbfacce --- /dev/null +++ b/src/internal/utils.ts @@ -0,0 +1,8 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './utils/values'; +export * from './utils/base64'; +export * from './utils/env'; +export * from './utils/log'; +export * from './utils/uuid'; +export * from './utils/sleep'; diff --git a/src/internal/utils/base64.ts b/src/internal/utils/base64.ts new file mode 100644 index 00000000..2312df37 --- /dev/null +++ b/src/internal/utils/base64.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { ImageKitError } from '../../core/error'; +import { encodeUTF8 } from './bytes'; + +export const toBase64 = (data: string | Uint8Array | null | undefined): string => { + if (!data) return ''; + + if (typeof (globalThis as any).Buffer !== 'undefined') { + return (globalThis as any).Buffer.from(data).toString('base64'); + } + + if (typeof data === 'string') { + data = encodeUTF8(data); + } + + if (typeof btoa !== 'undefined') { + return btoa(String.fromCharCode.apply(null, data as any)); + } + + throw new ImageKitError('Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined'); +}; + +export const fromBase64 = (str: string): Uint8Array => { + if (typeof (globalThis as any).Buffer !== 'undefined') { + const buf = (globalThis as any).Buffer.from(str, 'base64'); + return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); + } + + if (typeof atob !== 'undefined') { + const bstr = atob(str); + const buf = new Uint8Array(bstr.length); + for (let i = 0; i < bstr.length; i++) { + buf[i] = bstr.charCodeAt(i); + } + return buf; + } + + throw new ImageKitError('Cannot decode base64 string; Expected `Buffer` or `atob` to be defined'); +}; diff --git a/src/internal/utils/bytes.ts b/src/internal/utils/bytes.ts new file mode 100644 index 00000000..8da627ab --- /dev/null +++ b/src/internal/utils/bytes.ts @@ -0,0 +1,32 @@ +export function concatBytes(buffers: Uint8Array[]): Uint8Array { + let length = 0; + for (const buffer of buffers) { + length += buffer.length; + } + const output = new Uint8Array(length); + let index = 0; + for (const buffer of buffers) { + output.set(buffer, index); + index += buffer.length; + } + + return output; +} + +let encodeUTF8_: (str: string) => Uint8Array; +export function encodeUTF8(str: string) { + let encoder; + return ( + encodeUTF8_ ?? + ((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder))) + )(str); +} + +let decodeUTF8_: (bytes: Uint8Array) => string; +export function decodeUTF8(bytes: Uint8Array) { + let decoder; + return ( + decodeUTF8_ ?? + ((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder))) + )(bytes); +} diff --git a/src/internal/utils/env.ts b/src/internal/utils/env.ts new file mode 100644 index 00000000..2d848007 --- /dev/null +++ b/src/internal/utils/env.ts @@ -0,0 +1,18 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * Read an environment variable. + * + * Trims beginning and trailing whitespace. + * + * Will return undefined if the environment variable doesn't exist or cannot be accessed. + */ +export const readEnv = (env: string): string | undefined => { + if (typeof (globalThis as any).process !== 'undefined') { + return (globalThis as any).process.env?.[env]?.trim() ?? undefined; + } + if (typeof (globalThis as any).Deno !== 'undefined') { + return (globalThis as any).Deno.env?.get?.(env)?.trim(); + } + return undefined; +}; diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts new file mode 100644 index 00000000..7da41295 --- /dev/null +++ b/src/internal/utils/log.ts @@ -0,0 +1,126 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { hasOwn } from './values'; +import { type ImageKit } from '../../client'; +import { RequestOptions } from '../request-options'; + +type LogFn = (message: string, ...rest: unknown[]) => void; +export type Logger = { + error: LogFn; + warn: LogFn; + info: LogFn; + debug: LogFn; +}; +export type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug'; + +const levelNumbers = { + off: 0, + error: 200, + warn: 300, + info: 400, + debug: 500, +}; + +export const parseLogLevel = ( + maybeLevel: string | undefined, + sourceName: string, + client: ImageKit, +): LogLevel | undefined => { + if (!maybeLevel) { + return undefined; + } + if (hasOwn(levelNumbers, maybeLevel)) { + return maybeLevel; + } + loggerFor(client).warn( + `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify( + Object.keys(levelNumbers), + )}`, + ); + return undefined; +}; + +function noop() {} + +function makeLogFn(fnLevel: keyof Logger, logger: Logger | undefined, logLevel: LogLevel) { + if (!logger || levelNumbers[fnLevel] > levelNumbers[logLevel]) { + return noop; + } else { + // Don't wrap logger functions, we want the stacktrace intact! + return logger[fnLevel].bind(logger); + } +} + +const noopLogger = { + error: noop, + warn: noop, + info: noop, + debug: noop, +}; + +let cachedLoggers = /* @__PURE__ */ new WeakMap(); + +export function loggerFor(client: ImageKit): Logger { + const logger = client.logger; + const logLevel = client.logLevel ?? 'off'; + if (!logger) { + return noopLogger; + } + + const cachedLogger = cachedLoggers.get(logger); + if (cachedLogger && cachedLogger[0] === logLevel) { + return cachedLogger[1]; + } + + const levelLogger = { + error: makeLogFn('error', logger, logLevel), + warn: makeLogFn('warn', logger, logLevel), + info: makeLogFn('info', logger, logLevel), + debug: makeLogFn('debug', logger, logLevel), + }; + + cachedLoggers.set(logger, [logLevel, levelLogger]); + + return levelLogger; +} + +export const formatRequestDetails = (details: { + options?: RequestOptions | undefined; + headers?: Headers | Record | undefined; + retryOfRequestLogID?: string | undefined; + retryOf?: string | undefined; + url?: string | undefined; + status?: number | undefined; + method?: string | undefined; + durationMs?: number | undefined; + message?: unknown; + body?: unknown; +}) => { + if (details.options) { + details.options = { ...details.options }; + delete details.options['headers']; // redundant + leaks internals + } + if (details.headers) { + details.headers = Object.fromEntries( + (details.headers instanceof Headers ? [...details.headers] : Object.entries(details.headers)).map( + ([name, value]) => [ + name, + ( + name.toLowerCase() === 'authorization' || + name.toLowerCase() === 'cookie' || + name.toLowerCase() === 'set-cookie' + ) ? + '***' + : value, + ], + ), + ); + } + if ('retryOfRequestLogID' in details) { + if (details.retryOfRequestLogID) { + details.retryOf = details.retryOfRequestLogID; + } + delete details.retryOfRequestLogID; + } + return details; +}; diff --git a/src/internal/utils/path.ts b/src/internal/utils/path.ts new file mode 100644 index 00000000..dbf06649 --- /dev/null +++ b/src/internal/utils/path.ts @@ -0,0 +1,88 @@ +import { ImageKitError } from '../../core/error'; + +/** + * Percent-encode everything that isn't safe to have in a path without encoding safe chars. + * + * Taken from https://datatracker.ietf.org/doc/html/rfc3986#section-3.3: + * > unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * > sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + * > pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ +export function encodeURIPath(str: string) { + return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent); +} + +const EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null)); + +export const createPathTagFunction = (pathEncoder = encodeURIPath) => + function path(statics: readonly string[], ...params: readonly unknown[]): string { + // If there are no params, no processing is needed. + if (statics.length === 1) return statics[0]!; + + let postPath = false; + const invalidSegments = []; + const path = statics.reduce((previousValue, currentValue, index) => { + if (/[?#]/.test(currentValue)) { + postPath = true; + } + const value = params[index]; + let encoded = (postPath ? encodeURIComponent : pathEncoder)('' + value); + if ( + index !== params.length && + (value == null || + (typeof value === 'object' && + // handle values from other realms + value.toString === + Object.getPrototypeOf(Object.getPrototypeOf((value as any).hasOwnProperty ?? EMPTY) ?? EMPTY) + ?.toString)) + ) { + encoded = value + ''; + invalidSegments.push({ + start: previousValue.length + currentValue.length, + length: encoded.length, + error: `Value of type ${Object.prototype.toString + .call(value) + .slice(8, -1)} is not a valid path parameter`, + }); + } + return previousValue + currentValue + (index === params.length ? '' : encoded); + }, ''); + + const pathOnly = path.split(/[?#]/, 1)[0]!; + const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi; + let match; + + // Find all invalid segments + while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) { + invalidSegments.push({ + start: match.index, + length: match[0].length, + error: `Value "${match[0]}" can\'t be safely passed as a path parameter`, + }); + } + + invalidSegments.sort((a, b) => a.start - b.start); + + if (invalidSegments.length > 0) { + let lastEnd = 0; + const underline = invalidSegments.reduce((acc, segment) => { + const spaces = ' '.repeat(segment.start - lastEnd); + const arrows = '^'.repeat(segment.length); + lastEnd = segment.start + segment.length; + return acc + spaces + arrows; + }, ''); + + throw new ImageKitError( + `Path parameters result in path with invalid segments:\n${invalidSegments + .map((e) => e.error) + .join('\n')}\n${path}\n${underline}`, + ); + } + + return path; + }; + +/** + * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced. + */ +export const path = /* @__PURE__ */ createPathTagFunction(encodeURIPath); diff --git a/src/internal/utils/sleep.ts b/src/internal/utils/sleep.ts new file mode 100644 index 00000000..65e52962 --- /dev/null +++ b/src/internal/utils/sleep.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/internal/utils/uuid.ts b/src/internal/utils/uuid.ts new file mode 100644 index 00000000..b0e53aaf --- /dev/null +++ b/src/internal/utils/uuid.ts @@ -0,0 +1,17 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +/** + * https://stackoverflow.com/a/2117523 + */ +export let uuid4 = function () { + const { crypto } = globalThis as any; + if (crypto?.randomUUID) { + uuid4 = crypto.randomUUID.bind(crypto); + return crypto.randomUUID(); + } + const u8 = new Uint8Array(1); + const randomByte = crypto ? () => crypto.getRandomValues(u8)[0]! : () => (Math.random() * 0xff) & 0xff; + return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => + (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16), + ); +}; diff --git a/src/internal/utils/values.ts b/src/internal/utils/values.ts new file mode 100644 index 00000000..d73aadd0 --- /dev/null +++ b/src/internal/utils/values.ts @@ -0,0 +1,105 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { ImageKitError } from '../../core/error'; + +// https://url.spec.whatwg.org/#url-scheme-string +const startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i; + +export const isAbsoluteURL = (url: string): boolean => { + return startsWithSchemeRegexp.test(url); +}; + +export let isArray = (val: unknown): val is unknown[] => ((isArray = Array.isArray), isArray(val)); +export let isReadonlyArray = isArray as (val: unknown) => val is readonly unknown[]; + +/** Returns an object if the given value isn't an object, otherwise returns as-is */ +export function maybeObj(x: unknown): object { + if (typeof x !== 'object') { + return {}; + } + + return x ?? {}; +} + +// https://stackoverflow.com/a/34491287 +export function isEmptyObj(obj: Object | null | undefined): boolean { + if (!obj) return true; + for (const _k in obj) return false; + return true; +} + +// https://eslint.org/docs/latest/rules/no-prototype-builtins +export function hasOwn(obj: T, key: PropertyKey): key is keyof T { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +export function isObj(obj: unknown): obj is Record { + return obj != null && typeof obj === 'object' && !Array.isArray(obj); +} + +export const ensurePresent = (value: T | null | undefined): T => { + if (value == null) { + throw new ImageKitError(`Expected a value to be given but received ${value} instead.`); + } + + return value; +}; + +export const validatePositiveInteger = (name: string, n: unknown): number => { + if (typeof n !== 'number' || !Number.isInteger(n)) { + throw new ImageKitError(`${name} must be an integer`); + } + if (n < 0) { + throw new ImageKitError(`${name} must be a positive integer`); + } + return n; +}; + +export const coerceInteger = (value: unknown): number => { + if (typeof value === 'number') return Math.round(value); + if (typeof value === 'string') return parseInt(value, 10); + + throw new ImageKitError(`Could not coerce ${value} (type: ${typeof value}) into a number`); +}; + +export const coerceFloat = (value: unknown): number => { + if (typeof value === 'number') return value; + if (typeof value === 'string') return parseFloat(value); + + throw new ImageKitError(`Could not coerce ${value} (type: ${typeof value}) into a number`); +}; + +export const coerceBoolean = (value: unknown): boolean => { + if (typeof value === 'boolean') return value; + if (typeof value === 'string') return value === 'true'; + return Boolean(value); +}; + +export const maybeCoerceInteger = (value: unknown): number | undefined => { + if (value == null) { + return undefined; + } + return coerceInteger(value); +}; + +export const maybeCoerceFloat = (value: unknown): number | undefined => { + if (value == null) { + return undefined; + } + return coerceFloat(value); +}; + +export const maybeCoerceBoolean = (value: unknown): boolean | undefined => { + if (value == null) { + return undefined; + } + return coerceBoolean(value); +}; + +export const safeJSON = (text: string) => { + try { + return JSON.parse(text); + } catch (err) { + return undefined; + } +}; diff --git a/src/lib/.keep b/src/lib/.keep new file mode 100644 index 00000000..7554f8b2 --- /dev/null +++ b/src/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. diff --git a/src/lib/crypto-utils.ts b/src/lib/crypto-utils.ts new file mode 100644 index 00000000..174b4b82 --- /dev/null +++ b/src/lib/crypto-utils.ts @@ -0,0 +1,37 @@ +/** + * Simple synchronous crypto utilities for ImageKit SDK + * + * This module provides HMAC-SHA1 functionality using Node.js crypto module. + * URL signing is only supported in Node.js runtime. + */ + +import { ImageKitError } from '../core/error'; + +/** + * Creates an HMAC-SHA1 hash using Node.js crypto module + * + * @param key - The secret key for HMAC generation + * @param data - The data to be signed + * @returns Hex-encoded HMAC-SHA1 hash + * @throws ImageKitError if crypto module is not available or operation fails + */ +export function createHmacSha1(key: string, data: string): string { + let crypto: any; + + try { + crypto = require('crypto'); + } catch (err) { + throw new ImageKitError( + 'URL signing requires Node.js crypto module which is not available in this runtime. ' + + 'Please use Node.js environment for URL signing functionality.', + ); + } + + try { + return crypto.createHmac('sha1', key).update(data, 'utf8').digest('hex'); + } catch (error) { + throw new ImageKitError( + `Failed to generate HMAC-SHA1 signature: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + } +} diff --git a/src/lib/serialization-utils.ts b/src/lib/serialization-utils.ts new file mode 100644 index 00000000..4e94ada5 --- /dev/null +++ b/src/lib/serialization-utils.ts @@ -0,0 +1,42 @@ +/** + * Serialize upload options to handle proper formatting for ImageKit backend API. + * Special cases handled: + * - tags: converted to comma-separated string + * - responseFields: converted to comma-separated string + * - extensions: JSON stringified + * - customMetadata: JSON stringified + * - transformation: JSON stringified + */ +export function serializeUploadOptions(uploadOptions: Record): Record { + const serialized: Record = { ...uploadOptions }; + + for (const key in serialized) { + if (key && serialized[key] !== undefined) { + const value = serialized[key]; + + if (key === 'tags' && Array.isArray(value)) { + // Tags should be comma-separated string + serialized[key] = value.join(','); + } else if (key === 'responseFields' && Array.isArray(value)) { + // Response fields should be comma-separated string + serialized[key] = value.join(','); + } else if (key === 'extensions' && Array.isArray(value)) { + // Extensions should be JSON stringified + serialized[key] = JSON.stringify(value); + } else if ( + key === 'customMetadata' && + typeof value === 'object' && + !Array.isArray(value) && + value !== null + ) { + // Custom metadata should be JSON stringified + serialized[key] = JSON.stringify(value); + } else if (key === 'transformation' && typeof value === 'object' && value !== null) { + // Transformation should be JSON stringified + serialized[key] = JSON.stringify(value); + } + } + } + + return serialized; +} diff --git a/src/lib/transformation-utils.ts b/src/lib/transformation-utils.ts new file mode 100644 index 00000000..17fdf742 --- /dev/null +++ b/src/lib/transformation-utils.ts @@ -0,0 +1,117 @@ +// Transformation utilities ported from JavaScript SDK +// This file is in src/lib/ to avoid conflicts with generated code + +import type { SrcOptions, TransformationPosition } from '../resources/shared'; +import { toBase64 } from '../internal/utils/base64'; + +const QUERY_TRANSFORMATION_POSITION: TransformationPosition = 'query'; +const PATH_TRANSFORMATION_POSITION: TransformationPosition = 'path'; +const CHAIN_TRANSFORM_DELIMITER: string = ':'; +const TRANSFORM_DELIMITER: string = ','; +const TRANSFORM_KEY_VALUE_DELIMITER: string = '-'; + +/** + * Supported transformations mapping + * {@link https://imagekit.io/docs/transformations} + */ +export const supportedTransforms: { [key: string]: string } = { + // Basic sizing & layout + width: 'w', + height: 'h', + aspectRatio: 'ar', + background: 'bg', + border: 'b', + crop: 'c', + cropMode: 'cm', + dpr: 'dpr', + focus: 'fo', + quality: 'q', + x: 'x', + xCenter: 'xc', + y: 'y', + yCenter: 'yc', + format: 'f', + videoCodec: 'vc', + audioCodec: 'ac', + radius: 'r', + rotation: 'rt', + blur: 'bl', + named: 'n', + defaultImage: 'di', + flip: 'fl', + original: 'orig', + startOffset: 'so', + endOffset: 'eo', + duration: 'du', + streamingResolutions: 'sr', + + // AI & advanced effects + grayscale: 'e-grayscale', + aiUpscale: 'e-upscale', + aiRetouch: 'e-retouch', + aiVariation: 'e-genvar', + aiDropShadow: 'e-dropshadow', + aiChangeBackground: 'e-changebg', + aiRemoveBackground: 'e-bgremove', + aiRemoveBackgroundExternal: 'e-removedotbg', + aiEdit: 'e-edit', + contrastStretch: 'e-contrast', + shadow: 'e-shadow', + sharpen: 'e-sharpen', + unsharpMask: 'e-usm', + gradient: 'e-gradient', + + // Other flags & finishing + progressive: 'pr', + lossless: 'lo', + colorProfile: 'cp', + metadata: 'md', + opacity: 'o', + trim: 't', + zoom: 'z', + page: 'pg', + + // Text overlay transformations + fontSize: 'fs', + fontFamily: 'ff', + fontColor: 'co', + innerAlignment: 'ia', + padding: 'pa', + alpha: 'al', + typography: 'tg', + lineHeight: 'lh', + + // Subtitles transformations + fontOutline: 'fol', + fontShadow: 'fsh', + + // Raw pass-through + raw: 'raw', +}; + +export default { + addAsQueryParameter: (options: SrcOptions): boolean => { + return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; + }, + getTransformKey: function (transform: string): string { + if (!transform) { + return ''; + } + + return supportedTransforms[transform] || supportedTransforms[transform.toLowerCase()] || ''; + }, + getChainTransformDelimiter: function (): string { + return CHAIN_TRANSFORM_DELIMITER; + }, + getTransformDelimiter: function (): string { + return TRANSFORM_DELIMITER; + }, + getTransformKeyValueDelimiter: function (): string { + return TRANSFORM_KEY_VALUE_DELIMITER; + }, +}; + +export const safeBtoa = function (str: string): string { + // Use the SDK's built-in base64 utility that properly handles different runtimes + return toBase64(str); +}; diff --git a/src/resource.ts b/src/resource.ts new file mode 100644 index 00000000..363e3516 --- /dev/null +++ b/src/resource.ts @@ -0,0 +1,2 @@ +/** @deprecated Import from ./core/resource instead */ +export * from './core/resource'; diff --git a/src/resources.ts b/src/resources.ts new file mode 100644 index 00000000..b283d578 --- /dev/null +++ b/src/resources.ts @@ -0,0 +1 @@ +export * from './resources/index'; diff --git a/src/resources/accounts.ts b/src/resources/accounts.ts new file mode 100644 index 00000000..b9db2991 --- /dev/null +++ b/src/resources/accounts.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './accounts/index'; diff --git a/src/resources/accounts/accounts.ts b/src/resources/accounts/accounts.ts new file mode 100644 index 00000000..82d1b238 --- /dev/null +++ b/src/resources/accounts/accounts.ts @@ -0,0 +1,55 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as OriginsAPI from './origins'; +import { + OriginCreateParams, + OriginListResponse, + OriginRequest, + OriginResponse, + OriginUpdateParams, + Origins, +} from './origins'; +import * as URLEndpointsAPI from './url-endpoints'; +import { + URLEndpointCreateParams, + URLEndpointListResponse, + URLEndpointRequest, + URLEndpointResponse, + URLEndpointUpdateParams, + URLEndpoints, +} from './url-endpoints'; +import * as UsageAPI from './usage'; +import { Usage, UsageGetParams, UsageGetResponse } from './usage'; + +export class Accounts extends APIResource { + usage: UsageAPI.Usage = new UsageAPI.Usage(this._client); + origins: OriginsAPI.Origins = new OriginsAPI.Origins(this._client); + urlEndpoints: URLEndpointsAPI.URLEndpoints = new URLEndpointsAPI.URLEndpoints(this._client); +} + +Accounts.Usage = Usage; +Accounts.Origins = Origins; +Accounts.URLEndpoints = URLEndpoints; + +export declare namespace Accounts { + export { Usage as Usage, type UsageGetResponse as UsageGetResponse, type UsageGetParams as UsageGetParams }; + + export { + Origins as Origins, + type OriginRequest as OriginRequest, + type OriginResponse as OriginResponse, + type OriginListResponse as OriginListResponse, + type OriginCreateParams as OriginCreateParams, + type OriginUpdateParams as OriginUpdateParams, + }; + + export { + URLEndpoints as URLEndpoints, + type URLEndpointRequest as URLEndpointRequest, + type URLEndpointResponse as URLEndpointResponse, + type URLEndpointListResponse as URLEndpointListResponse, + type URLEndpointCreateParams as URLEndpointCreateParams, + type URLEndpointUpdateParams as URLEndpointUpdateParams, + }; +} diff --git a/src/resources/accounts/index.ts b/src/resources/accounts/index.ts new file mode 100644 index 00000000..34a0246d --- /dev/null +++ b/src/resources/accounts/index.ts @@ -0,0 +1,20 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { Accounts } from './accounts'; +export { + Origins, + type OriginRequest, + type OriginResponse, + type OriginListResponse, + type OriginCreateParams, + type OriginUpdateParams, +} from './origins'; +export { + URLEndpoints, + type URLEndpointRequest, + type URLEndpointResponse, + type URLEndpointListResponse, + type URLEndpointCreateParams, + type URLEndpointUpdateParams, +} from './url-endpoints'; +export { Usage, type UsageGetResponse, type UsageGetParams } from './usage'; diff --git a/src/resources/accounts/origins.ts b/src/resources/accounts/origins.ts new file mode 100644 index 00000000..f59a468e --- /dev/null +++ b/src/resources/accounts/origins.ts @@ -0,0 +1,1250 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Origins extends APIResource { + /** + * **Note:** This API is currently in beta. + * Creates a new origin and returns the origin object. + * + * @example + * ```ts + * const originResponse = await client.accounts.origins.create( + * { + * accessKey: 'AKIAIOSFODNN7EXAMPLE', + * bucket: 'product-images', + * name: 'US S3 Storage', + * secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + * type: 'S3', + * }, + * ); + * ``` + */ + create(body: OriginCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/accounts/origins', { body, ...options }); + } + + /** + * **Note:** This API is currently in beta. + * Updates the origin identified by `id` and returns the updated origin object. + * + * @example + * ```ts + * const originResponse = await client.accounts.origins.update( + * 'id', + * { + * accessKey: 'AKIAIOSFODNN7EXAMPLE', + * bucket: 'product-images', + * name: 'US S3 Storage', + * secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + * type: 'S3', + * }, + * ); + * ``` + */ + update(id: string, body: OriginUpdateParams, options?: RequestOptions): APIPromise { + return this._client.put(path`/v1/accounts/origins/${id}`, { body, ...options }); + } + + /** + * **Note:** This API is currently in beta. + * Returns an array of all configured origins for the current account. + * + * @example + * ```ts + * const originResponses = + * await client.accounts.origins.list(); + * ``` + */ + list(options?: RequestOptions): APIPromise { + return this._client.get('/v1/accounts/origins', options); + } + + /** + * **Note:** This API is currently in beta. + * Permanently removes the origin identified by `id`. If the origin is in use by + * any URL‑endpoints, the API will return an error. + * + * @example + * ```ts + * await client.accounts.origins.delete('id'); + * ``` + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v1/accounts/origins/${id}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } + + /** + * **Note:** This API is currently in beta. + * Retrieves the origin identified by `id`. + * + * @example + * ```ts + * const originResponse = await client.accounts.origins.get( + * 'id', + * ); + * ``` + */ + get(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/accounts/origins/${id}`, options); + } +} + +/** + * Schema for origin request resources. + */ +export type OriginRequest = + | OriginRequest.S3 + | OriginRequest.S3Compatible + | OriginRequest.CloudinaryBackup + | OriginRequest.WebFolder + | OriginRequest.WebProxy + | OriginRequest.Gcs + | OriginRequest.AzureBlob + | OriginRequest.AkeneoPim; + +export namespace OriginRequest { + export interface S3 { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface S3Compatible { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Custom S3-compatible endpoint. + */ + endpoint: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3_COMPATIBLE'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + + /** + * Use path-style S3 URLs? + */ + s3ForcePathStyle?: boolean; + } + + export interface CloudinaryBackup { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'CLOUDINARY_BACKUP'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface WebFolder { + /** + * Root URL for the web folder origin. + */ + baseUrl: string; + + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_FOLDER'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Forward the Host header to origin? + */ + forwardHostHeaderToOrigin?: boolean; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface WebProxy { + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_PROXY'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface Gcs { + bucket: string; + + clientEmail: string; + + /** + * Display name of the origin. + */ + name: string; + + privateKey: string; + + type: 'GCS'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AzureBlob { + accountName: string; + + container: string; + + /** + * Display name of the origin. + */ + name: string; + + sasToken: string; + + type: 'AZURE_BLOB'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AkeneoPim { + /** + * Akeneo instance base URL. + */ + baseUrl: string; + + /** + * Akeneo API client ID. + */ + clientId: string; + + /** + * Akeneo API client secret. + */ + clientSecret: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Akeneo API password. + */ + password: string; + + type: 'AKENEO_PIM'; + + /** + * Akeneo API username. + */ + username: string; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } +} + +/** + * Origin object as returned by the API (sensitive fields removed). + */ +export type OriginResponse = + | OriginResponse.S3 + | OriginResponse.S3Compatible + | OriginResponse.CloudinaryBackup + | OriginResponse.WebFolder + | OriginResponse.WebProxy + | OriginResponse.Gcs + | OriginResponse.AzureBlob + | OriginResponse.AkeneoPim; + +export namespace OriginResponse { + export interface S3 { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Path prefix inside the bucket. + */ + prefix: string; + + type: 'S3'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface S3Compatible { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Custom S3-compatible endpoint. + */ + endpoint: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Path prefix inside the bucket. + */ + prefix: string; + + /** + * Use path-style S3 URLs? + */ + s3ForcePathStyle: boolean; + + type: 'S3_COMPATIBLE'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface CloudinaryBackup { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Path prefix inside the bucket. + */ + prefix: string; + + type: 'CLOUDINARY_BACKUP'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface WebFolder { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * Root URL for the web folder origin. + */ + baseUrl: string; + + /** + * Forward the Host header to origin? + */ + forwardHostHeaderToOrigin: boolean; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_FOLDER'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface WebProxy { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_PROXY'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface Gcs { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + bucket: string; + + clientEmail: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + prefix: string; + + type: 'GCS'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface AzureBlob { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + accountName: string; + + container: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + prefix: string; + + type: 'AZURE_BLOB'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } + + export interface AkeneoPim { + /** + * Unique identifier for the origin. This is generated by ImageKit when you create + * a new origin. + */ + id: string; + + /** + * Akeneo instance base URL. + */ + baseUrl: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader: boolean; + + /** + * Display name of the origin. + */ + name: string; + + type: 'AKENEO_PIM'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + } +} + +export type OriginListResponse = Array; + +export type OriginCreateParams = + | OriginCreateParams.S3 + | OriginCreateParams.S3Compatible + | OriginCreateParams.CloudinaryBackup + | OriginCreateParams.WebFolder + | OriginCreateParams.WebProxy + | OriginCreateParams.GoogleCloudStorageGcs + | OriginCreateParams.AzureBlobStorage + | OriginCreateParams.AkeneoPim; + +export declare namespace OriginCreateParams { + export interface S3 { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface S3Compatible { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Custom S3-compatible endpoint. + */ + endpoint: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3_COMPATIBLE'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + + /** + * Use path-style S3 URLs? + */ + s3ForcePathStyle?: boolean; + } + + export interface CloudinaryBackup { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'CLOUDINARY_BACKUP'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface WebFolder { + /** + * Root URL for the web folder origin. + */ + baseUrl: string; + + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_FOLDER'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Forward the Host header to origin? + */ + forwardHostHeaderToOrigin?: boolean; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface WebProxy { + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_PROXY'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface GoogleCloudStorageGcs { + bucket: string; + + clientEmail: string; + + /** + * Display name of the origin. + */ + name: string; + + privateKey: string; + + type: 'GCS'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AzureBlobStorage { + accountName: string; + + container: string; + + /** + * Display name of the origin. + */ + name: string; + + sasToken: string; + + type: 'AZURE_BLOB'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AkeneoPim { + /** + * Akeneo instance base URL. + */ + baseUrl: string; + + /** + * Akeneo API client ID. + */ + clientId: string; + + /** + * Akeneo API client secret. + */ + clientSecret: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Akeneo API password. + */ + password: string; + + type: 'AKENEO_PIM'; + + /** + * Akeneo API username. + */ + username: string; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } +} + +export type OriginUpdateParams = + | OriginUpdateParams.S3 + | OriginUpdateParams.S3Compatible + | OriginUpdateParams.CloudinaryBackup + | OriginUpdateParams.WebFolder + | OriginUpdateParams.WebProxy + | OriginUpdateParams.GoogleCloudStorageGcs + | OriginUpdateParams.AzureBlobStorage + | OriginUpdateParams.AkeneoPim; + +export declare namespace OriginUpdateParams { + export interface S3 { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface S3Compatible { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Custom S3-compatible endpoint. + */ + endpoint: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'S3_COMPATIBLE'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + + /** + * Use path-style S3 URLs? + */ + s3ForcePathStyle?: boolean; + } + + export interface CloudinaryBackup { + /** + * Access key for the bucket. + */ + accessKey: string; + + /** + * S3 bucket name. + */ + bucket: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Secret key for the bucket. + */ + secretKey: string; + + type: 'CLOUDINARY_BACKUP'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + /** + * Path prefix inside the bucket. + */ + prefix?: string; + } + + export interface WebFolder { + /** + * Root URL for the web folder origin. + */ + baseUrl: string; + + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_FOLDER'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Forward the Host header to origin? + */ + forwardHostHeaderToOrigin?: boolean; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface WebProxy { + /** + * Display name of the origin. + */ + name: string; + + type: 'WEB_PROXY'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } + + export interface GoogleCloudStorageGcs { + bucket: string; + + clientEmail: string; + + /** + * Display name of the origin. + */ + name: string; + + privateKey: string; + + type: 'GCS'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AzureBlobStorage { + accountName: string; + + container: string; + + /** + * Display name of the origin. + */ + name: string; + + sasToken: string; + + type: 'AZURE_BLOB'; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + + prefix?: string; + } + + export interface AkeneoPim { + /** + * Akeneo instance base URL. + */ + baseUrl: string; + + /** + * Akeneo API client ID. + */ + clientId: string; + + /** + * Akeneo API client secret. + */ + clientSecret: string; + + /** + * Display name of the origin. + */ + name: string; + + /** + * Akeneo API password. + */ + password: string; + + type: 'AKENEO_PIM'; + + /** + * Akeneo API username. + */ + username: string; + + /** + * URL used in the Canonical header (if enabled). + */ + baseUrlForCanonicalHeader?: string; + + /** + * Whether to send a Canonical header. + */ + includeCanonicalHeader?: boolean; + } +} + +export declare namespace Origins { + export { + type OriginRequest as OriginRequest, + type OriginResponse as OriginResponse, + type OriginListResponse as OriginListResponse, + type OriginCreateParams as OriginCreateParams, + type OriginUpdateParams as OriginUpdateParams, + }; +} diff --git a/src/resources/accounts/url-endpoints.ts b/src/resources/accounts/url-endpoints.ts new file mode 100644 index 00000000..7bd424c4 --- /dev/null +++ b/src/resources/accounts/url-endpoints.ts @@ -0,0 +1,298 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class URLEndpoints extends APIResource { + /** + * **Note:** This API is currently in beta. + * Creates a new URL‑endpoint and returns the resulting object. + * + * @example + * ```ts + * const urlEndpointResponse = + * await client.accounts.urlEndpoints.create({ + * description: 'My custom URL endpoint', + * }); + * ``` + */ + create(body: URLEndpointCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/accounts/url-endpoints', { body, ...options }); + } + + /** + * **Note:** This API is currently in beta. + * Updates the URL‑endpoint identified by `id` and returns the updated object. + * + * @example + * ```ts + * const urlEndpointResponse = + * await client.accounts.urlEndpoints.update('id', { + * description: 'My custom URL endpoint', + * }); + * ``` + */ + update( + id: string, + body: URLEndpointUpdateParams, + options?: RequestOptions, + ): APIPromise { + return this._client.put(path`/v1/accounts/url-endpoints/${id}`, { body, ...options }); + } + + /** + * **Note:** This API is currently in beta. + * Returns an array of all URL‑endpoints configured including the default + * URL-endpoint generated by ImageKit during account creation. + * + * @example + * ```ts + * const urlEndpointResponses = + * await client.accounts.urlEndpoints.list(); + * ``` + */ + list(options?: RequestOptions): APIPromise { + return this._client.get('/v1/accounts/url-endpoints', options); + } + + /** + * **Note:** This API is currently in beta. + * Deletes the URL‑endpoint identified by `id`. You cannot delete the default + * URL‑endpoint created by ImageKit during account creation. + * + * @example + * ```ts + * await client.accounts.urlEndpoints.delete('id'); + * ``` + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v1/accounts/url-endpoints/${id}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } + + /** + * **Note:** This API is currently in beta. + * Retrieves the URL‑endpoint identified by `id`. + * + * @example + * ```ts + * const urlEndpointResponse = + * await client.accounts.urlEndpoints.get('id'); + * ``` + */ + get(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/accounts/url-endpoints/${id}`, options); + } +} + +/** + * Schema for URL endpoint resource. + */ +export interface URLEndpointRequest { + /** + * Description of the URL endpoint. + */ + description: string; + + /** + * Ordered list of origin IDs to try when the file isn’t in the Media Library; + * ImageKit checks them in the sequence provided. Origin must be created before it + * can be used in a URL endpoint. + */ + origins?: Array; + + /** + * Path segment appended to your base URL to form the endpoint (letters, digits, + * and hyphens only — or empty for the default endpoint). + */ + urlPrefix?: string; + + /** + * Configuration for third-party URL rewriting. + */ + urlRewriter?: URLEndpointRequest.Cloudinary | URLEndpointRequest.Imgix | URLEndpointRequest.Akamai; +} + +export namespace URLEndpointRequest { + export interface Cloudinary { + type: 'CLOUDINARY'; + + /** + * Whether to preserve `/` in the rewritten URL. + */ + preserveAssetDeliveryTypes?: boolean; + } + + export interface Imgix { + type: 'IMGIX'; + } + + export interface Akamai { + type: 'AKAMAI'; + } +} + +/** + * URL‑endpoint object as returned by the API. + */ +export interface URLEndpointResponse { + /** + * Unique identifier for the URL-endpoint. This is generated by ImageKit when you + * create a new URL-endpoint. For the default URL-endpoint, this is always + * `default`. + */ + id: string; + + /** + * Description of the URL endpoint. + */ + description: string; + + /** + * Ordered list of origin IDs to try when the file isn’t in the Media Library; + * ImageKit checks them in the sequence provided. Origin must be created before it + * can be used in a URL endpoint. + */ + origins: Array; + + /** + * Path segment appended to your base URL to form the endpoint (letters, digits, + * and hyphens only — or empty for the default endpoint). + */ + urlPrefix: string; + + /** + * Configuration for third-party URL rewriting. + */ + urlRewriter?: URLEndpointResponse.Cloudinary | URLEndpointResponse.Imgix | URLEndpointResponse.Akamai; +} + +export namespace URLEndpointResponse { + export interface Cloudinary { + /** + * Whether to preserve `/` in the rewritten URL. + */ + preserveAssetDeliveryTypes: boolean; + + type: 'CLOUDINARY'; + } + + export interface Imgix { + type: 'IMGIX'; + } + + export interface Akamai { + type: 'AKAMAI'; + } +} + +export type URLEndpointListResponse = Array; + +export interface URLEndpointCreateParams { + /** + * Description of the URL endpoint. + */ + description: string; + + /** + * Ordered list of origin IDs to try when the file isn’t in the Media Library; + * ImageKit checks them in the sequence provided. Origin must be created before it + * can be used in a URL endpoint. + */ + origins?: Array; + + /** + * Path segment appended to your base URL to form the endpoint (letters, digits, + * and hyphens only — or empty for the default endpoint). + */ + urlPrefix?: string; + + /** + * Configuration for third-party URL rewriting. + */ + urlRewriter?: + | URLEndpointCreateParams.Cloudinary + | URLEndpointCreateParams.Imgix + | URLEndpointCreateParams.Akamai; +} + +export namespace URLEndpointCreateParams { + export interface Cloudinary { + type: 'CLOUDINARY'; + + /** + * Whether to preserve `/` in the rewritten URL. + */ + preserveAssetDeliveryTypes?: boolean; + } + + export interface Imgix { + type: 'IMGIX'; + } + + export interface Akamai { + type: 'AKAMAI'; + } +} + +export interface URLEndpointUpdateParams { + /** + * Description of the URL endpoint. + */ + description: string; + + /** + * Ordered list of origin IDs to try when the file isn’t in the Media Library; + * ImageKit checks them in the sequence provided. Origin must be created before it + * can be used in a URL endpoint. + */ + origins?: Array; + + /** + * Path segment appended to your base URL to form the endpoint (letters, digits, + * and hyphens only — or empty for the default endpoint). + */ + urlPrefix?: string; + + /** + * Configuration for third-party URL rewriting. + */ + urlRewriter?: + | URLEndpointUpdateParams.Cloudinary + | URLEndpointUpdateParams.Imgix + | URLEndpointUpdateParams.Akamai; +} + +export namespace URLEndpointUpdateParams { + export interface Cloudinary { + type: 'CLOUDINARY'; + + /** + * Whether to preserve `/` in the rewritten URL. + */ + preserveAssetDeliveryTypes?: boolean; + } + + export interface Imgix { + type: 'IMGIX'; + } + + export interface Akamai { + type: 'AKAMAI'; + } +} + +export declare namespace URLEndpoints { + export { + type URLEndpointRequest as URLEndpointRequest, + type URLEndpointResponse as URLEndpointResponse, + type URLEndpointListResponse as URLEndpointListResponse, + type URLEndpointCreateParams as URLEndpointCreateParams, + type URLEndpointUpdateParams as URLEndpointUpdateParams, + }; +} diff --git a/src/resources/accounts/usage.ts b/src/resources/accounts/usage.ts new file mode 100644 index 00000000..87e703a4 --- /dev/null +++ b/src/resources/accounts/usage.ts @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Usage extends APIResource { + /** + * Get the account usage information between two dates. Note that the API response + * includes data from the start date while excluding data from the end date. In + * other words, the data covers the period starting from the specified start date + * up to, but not including, the end date. + * + * @example + * ```ts + * const usage = await client.accounts.usage.get({ + * endDate: '2019-12-27', + * startDate: '2019-12-27', + * }); + * ``` + */ + get(query: UsageGetParams, options?: RequestOptions): APIPromise { + return this._client.get('/v1/accounts/usage', { query, ...options }); + } +} + +export interface UsageGetResponse { + /** + * Amount of bandwidth used in bytes. + */ + bandwidthBytes?: number; + + /** + * Number of extension units used. + */ + extensionUnitsCount?: number; + + /** + * Storage used by media library in bytes. + */ + mediaLibraryStorageBytes?: number; + + /** + * Storage used by the original cache in bytes. + */ + originalCacheStorageBytes?: number; + + /** + * Number of video processing units used. + */ + videoProcessingUnitsCount?: number; +} + +export interface UsageGetParams { + /** + * Specify a `endDate` in `YYYY-MM-DD` format. It should be after the `startDate`. + * The difference between `startDate` and `endDate` should be less than 90 days. + */ + endDate: string; + + /** + * Specify a `startDate` in `YYYY-MM-DD` format. It should be before the `endDate`. + * The difference between `startDate` and `endDate` should be less than 90 days. + */ + startDate: string; +} + +export declare namespace Usage { + export { type UsageGetResponse as UsageGetResponse, type UsageGetParams as UsageGetParams }; +} diff --git a/src/resources/assets.ts b/src/resources/assets.ts new file mode 100644 index 00000000..19a35adb --- /dev/null +++ b/src/resources/assets.ts @@ -0,0 +1,105 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import * as FilesAPI from './files/files'; +import { APIPromise } from '../core/api-promise'; +import { RequestOptions } from '../internal/request-options'; + +export class Assets extends APIResource { + /** + * This API can list all the uploaded files and folders in your ImageKit.io media + * library. In addition, you can fine-tune your query by specifying various filters + * by generating a query string in a Lucene-like syntax and provide this generated + * string as the value of the `searchQuery`. + */ + list( + query: AssetListParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v1/files', { query, ...options }); + } +} + +export type AssetListResponse = Array; + +export interface AssetListParams { + /** + * Filter results by file type. + * + * - `all` — include all file types + * - `image` — include only image files + * - `non-image` — include only non-image files (e.g., JS, CSS, video) + */ + fileType?: 'all' | 'image' | 'non-image'; + + /** + * The maximum number of results to return in response. + */ + limit?: number; + + /** + * Folder path if you want to limit the search within a specific folder. For + * example, `/sales-banner/` will only search in folder sales-banner. + * + * Note : If your use case involves searching within a folder as well as its + * subfolders, you can use `path` parameter in `searchQuery` with appropriate + * operator. Checkout + * [Supported parameters](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#supported-parameters) + * for more information. + */ + path?: string; + + /** + * Query string in a Lucene-like query language e.g. `createdAt > "7d"`. + * + * Note : When the searchQuery parameter is present, the following query parameters + * will have no effect on the result: + * + * 1. `tags` + * 2. `type` + * 3. `name` + * + * [Learn more](/docs/api-reference/digital-asset-management-dam/list-and-search-assets#advanced-search-queries) + * from examples. + */ + searchQuery?: string; + + /** + * The number of results to skip before returning results. + */ + skip?: number; + + /** + * Sort the results by one of the supported fields in ascending or descending + * order. + */ + sort?: + | 'ASC_NAME' + | 'DESC_NAME' + | 'ASC_CREATED' + | 'DESC_CREATED' + | 'ASC_UPDATED' + | 'DESC_UPDATED' + | 'ASC_HEIGHT' + | 'DESC_HEIGHT' + | 'ASC_WIDTH' + | 'DESC_WIDTH' + | 'ASC_SIZE' + | 'DESC_SIZE' + | 'ASC_RELEVANCE' + | 'DESC_RELEVANCE'; + + /** + * Filter results by asset type. + * + * - `file` — returns only files + * - `file-version` — returns specific file versions + * - `folder` — returns only folders + * - `all` — returns both files and folders (excludes `file-version`) + */ + type?: 'file' | 'file-version' | 'folder' | 'all'; +} + +export declare namespace Assets { + export { type AssetListResponse as AssetListResponse, type AssetListParams as AssetListParams }; +} diff --git a/src/resources/beta.ts b/src/resources/beta.ts new file mode 100644 index 00000000..1542e942 --- /dev/null +++ b/src/resources/beta.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './beta/index'; diff --git a/src/resources/beta/beta.ts b/src/resources/beta/beta.ts new file mode 100644 index 00000000..f8fa04c2 --- /dev/null +++ b/src/resources/beta/beta.ts @@ -0,0 +1,15 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as V2API from './v2/v2'; +import { V2 } from './v2/v2'; + +export class Beta extends APIResource { + v2: V2API.V2 = new V2API.V2(this._client); +} + +Beta.V2 = V2; + +export declare namespace Beta { + export { V2 as V2 }; +} diff --git a/src/resources/beta/index.ts b/src/resources/beta/index.ts new file mode 100644 index 00000000..2b3a43ce --- /dev/null +++ b/src/resources/beta/index.ts @@ -0,0 +1,4 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { Beta } from './beta'; +export { V2 } from './v2/index'; diff --git a/src/resources/beta/v2.ts b/src/resources/beta/v2.ts new file mode 100644 index 00000000..ca56a446 --- /dev/null +++ b/src/resources/beta/v2.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './v2/index'; diff --git a/src/resources/beta/v2/files.ts b/src/resources/beta/v2/files.ts new file mode 100644 index 00000000..95ef6760 --- /dev/null +++ b/src/resources/beta/v2/files.ts @@ -0,0 +1,546 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../../core/resource'; +import * as Shared from '../../shared'; +import * as FilesAPI from '../../files/files'; +import { APIPromise } from '../../../core/api-promise'; +import { type Uploadable } from '../../../core/uploads'; +import { RequestOptions } from '../../../internal/request-options'; +import { multipartFormRequestOptions } from '../../../internal/uploads'; +import { serializeUploadOptions } from '../../../lib/serialization-utils'; + +export class Files extends APIResource { + /** + * The V2 API enhances security by verifying the entire payload using JWT. This API + * is in beta. + * + * ImageKit.io allows you to upload files directly from both the server and client + * sides. For server-side uploads, private API key authentication is used. For + * client-side uploads, generate a one-time `token` from your secure backend using + * private API. + * [Learn more](/docs/api-reference/upload-file/upload-file-v2#how-to-implement-secure-client-side-file-upload) + * about how to implement secure client-side file upload. + * + * **File size limit** \ + * On the free plan, the maximum upload file sizes are 20MB for images, audio, and raw + * files, and 100MB for videos. On the paid plan, these limits increase to 40MB for + * images, audio, and raw files, and 2GB for videos. These limits can be further increased + * with higher-tier plans. + * + * **Version limit** \ + * A file can have a maximum of 100 versions. + * + * **Demo applications** + * + * - A full-fledged + * [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), + * supporting file selections from local storage, URL, Dropbox, Google Drive, + * Instagram, and more. + * - [Quick start guides](/docs/quick-start-guides) for various frameworks and + * technologies. + * + * @example + * ```ts + * const response = await client.beta.v2.files.upload({ + * file: fs.createReadStream('path/to/file'), + * fileName: 'fileName', + * }); + * ``` + */ + upload(body: FileUploadParams, options?: RequestOptions): APIPromise { + const serializedBody = serializeUploadOptions(body); + + return this._client.post( + '/api/v2/files/upload', + multipartFormRequestOptions( + { body: serializedBody, defaultBaseURL: 'https://upload.imagekit.io', ...options }, + this._client, + ), + ); + } +} + +/** + * Object containing details of a successful upload. + */ +export interface FileUploadResponse { + /** + * An array of tags assigned to the uploaded file by auto tagging. + */ + AITags?: Array | null; + + /** + * The audio codec used in the video (only for video). + */ + audioCodec?: string; + + /** + * The bit rate of the video in kbps (only for video). + */ + bitRate?: number; + + /** + * Value of custom coordinates associated with the image in the format + * `x,y,width,height`. If `customCoordinates` are not defined, then it is `null`. + * Send `customCoordinates` in `responseFields` in API request to get the value of + * this field. + */ + customCoordinates?: string | null; + + /** + * A key-value data associated with the asset. Use `responseField` in API request + * to get `customMetadata` in the upload API response. Before setting any custom + * metadata on an asset, you have to create the field using custom metadata fields + * API. Send `customMetadata` in `responseFields` in API request to get the value + * of this field. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. Can be set by the user or + * the ai-auto-description extension. + */ + description?: string; + + /** + * The duration of the video in seconds (only for video). + */ + duration?: number; + + /** + * Consolidated embedded metadata associated with the file. It includes exif, iptc, + * and xmp data. Send `embeddedMetadata` in `responseFields` in API request to get + * embeddedMetadata in the upload API response. + */ + embeddedMetadata?: { [key: string]: unknown }; + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + extensionStatus?: FileUploadResponse.ExtensionStatus; + + /** + * Unique fileId. Store this fileld in your database, as this will be used to + * perform update action on this file. + */ + fileId?: string; + + /** + * The relative path of the file in the media library e.g. + * `/marketing-assets/new-banner.jpg`. + */ + filePath?: string; + + /** + * Type of the uploaded file. Possible values are `image`, `non-image`. + */ + fileType?: string; + + /** + * Height of the image in pixels (Only for images) + */ + height?: number; + + /** + * Is the file marked as private. It can be either `true` or `false`. Send + * `isPrivateFile` in `responseFields` in API request to get the value of this + * field. + */ + isPrivateFile?: boolean; + + /** + * Is the file published or in draft state. It can be either `true` or `false`. + * Send `isPublished` in `responseFields` in API request to get the value of this + * field. + */ + isPublished?: boolean; + + /** + * Legacy metadata. Send `metadata` in `responseFields` in API request to get + * metadata in the upload API response. + */ + metadata?: FilesAPI.Metadata; + + /** + * Name of the asset. + */ + name?: string; + + /** + * Size of the image file in Bytes. + */ + size?: number; + + /** + * The array of tags associated with the asset. If no tags are set, it will be + * `null`. Send `tags` in `responseFields` in API request to get the value of this + * field. + */ + tags?: Array | null; + + /** + * In the case of an image, a small thumbnail URL. + */ + thumbnailUrl?: string; + + /** + * A publicly accessible URL of the file. + */ + url?: string; + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + versionInfo?: FileUploadResponse.VersionInfo; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * Width of the image in pixels (Only for Images) + */ + width?: number; +} + +export namespace FileUploadResponse { + export interface AITag { + /** + * Confidence score of the tag. + */ + confidence?: number; + + /** + * Name of the tag. + */ + name?: string; + + /** + * Array of `AITags` associated with the image. If no `AITags` are set, it will be + * null. These tags can be added using the `google-auto-tagging` or + * `aws-auto-tagging` extensions. + */ + source?: string; + } + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + export interface ExtensionStatus { + 'ai-auto-description'?: 'success' | 'pending' | 'failed'; + + 'aws-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'google-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'remove-bg'?: 'success' | 'pending' | 'failed'; + } + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + export interface VersionInfo { + /** + * Unique identifier of the file version. + */ + id?: string; + + /** + * Name of the file version. + */ + name?: string; + } +} + +export interface FileUploadParams { + /** + * The API accepts any of the following: + * + * - **Binary data** – send the raw bytes as `multipart/form-data`. + * - **HTTP / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can + * fetch. + * - **Base64 string** – the file encoded as a Base64 data URI or plain Base64. + * + * When supplying a URL, the server must receive the response headers within 8 + * seconds; otherwise the request fails with 400 Bad Request. + */ + file: Uploadable | string; + + /** + * The name with which the file has to be uploaded. + */ + fileName: string; + + /** + * This is the client-generated JSON Web Token (JWT). The ImageKit.io server uses + * it to authenticate and check that the upload request parameters have not been + * tampered with after the token has been generated. Learn how to create the token + * on the page below. This field is only required for authentication when uploading + * a file from the client side. + * + * **Note**: Sending a JWT that has been used in the past will result in a + * validation error. Even if your previous request resulted in an error, you should + * always send a new token. + * + * **⚠️Warning**: JWT must be generated on the server-side because it is generated + * using your account's private API key. This field is required for authentication + * when uploading a file from the client-side. + */ + token?: string; + + /** + * Server-side checks to run on the asset. Read more about + * [Upload API checks](/docs/api-reference/upload-file/upload-file-v2#upload-api-checks). + */ + checks?: string; + + /** + * Define an important area in the image. This is only relevant for image type + * files. + * + * - To be passed as a string with the x and y coordinates of the top-left corner, + * and width and height of the area of interest in the format `x,y,width,height`. + * For example - `10,10,100,100` + * - Can be used with fo-customtransformation. + * - If this field is not specified and the file is overwritten, then + * customCoordinates will be removed. + */ + customCoordinates?: string; + + /** + * JSON key-value pairs to associate with the asset. Create the custom metadata + * fields before setting these values. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. + */ + description?: string; + + /** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ + extensions?: Shared.Extensions; + + /** + * The folder path in which the image has to be uploaded. If the folder(s) didn't + * exist before, a new folder(s) is created. Using multiple `/` creates a nested + * folder. + */ + folder?: string; + + /** + * Whether to mark the file as private or not. + * + * If `true`, the file is marked as private and is accessible only using named + * transformation or signed URL. + */ + isPrivateFile?: boolean; + + /** + * Whether to upload file as published or not. + * + * If `false`, the file is marked as unpublished, which restricts access to the + * file only via the media library. Files in draft or unpublished state can only be + * publicly accessed after being published. + * + * The option to upload in draft state is only available in custom enterprise + * pricing plans. + */ + isPublished?: boolean; + + /** + * If set to `true` and a file already exists at the exact location, its AITags + * will be removed. Set `overwriteAITags` to `false` to preserve AITags. + */ + overwriteAITags?: boolean; + + /** + * If the request does not have `customMetadata`, and a file already exists at the + * exact location, existing customMetadata will be removed. + */ + overwriteCustomMetadata?: boolean; + + /** + * If `false` and `useUniqueFileName` is also `false`, and a file already exists at + * the exact location, upload API will return an error immediately. + */ + overwriteFile?: boolean; + + /** + * If the request does not have `tags`, and a file already exists at the exact + * location, existing tags will be removed. + */ + overwriteTags?: boolean; + + /** + * Array of response field keys to include in the API response body. + */ + responseFields?: Array< + | 'tags' + | 'customCoordinates' + | 'isPrivateFile' + | 'embeddedMetadata' + | 'isPublished' + | 'customMetadata' + | 'metadata' + >; + + /** + * Set the tags while uploading the file. Provide an array of tag strings (e.g. + * `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must not + * exceed 500, and the `%` character is not allowed. If this field is not specified + * and the file is overwritten, the existing tags will be removed. + */ + tags?: Array; + + /** + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. + */ + transformation?: FileUploadParams.Transformation; + + /** + * Whether to use a unique filename for this file or not. + * + * If `true`, ImageKit.io will add a unique suffix to the filename parameter to get + * a unique filename. + * + * If `false`, then the image is uploaded with the provided filename parameter, and + * any existing file with the same name is replaced. + */ + useUniqueFileName?: boolean; + + /** + * The final status of extensions after they have completed execution will be + * delivered to this endpoint as a POST request. + * [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + * about the webhook payload structure. + */ + webhookUrl?: string; +} + +export namespace FileUploadParams { + /** + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. + */ + export interface Transformation { + /** + * List of transformations to apply _after_ the file is uploaded. + * Each item must match one of the following types: `transformation`, + * `gif-to-video`, `thumbnail`, `abs`. + */ + post?: Array< + | Transformation.Transformation + | Transformation.GifToVideo + | Transformation.Thumbnail + | Transformation.Abs + >; + + /** + * Transformation string to apply before uploading the file to the Media Library. + * Useful for optimizing files at ingestion. + */ + pre?: string; + } + + export namespace Transformation { + export interface Transformation { + /** + * Transformation type. + */ + type: 'transformation'; + + /** + * Transformation string (e.g. `w-200,h-200`). + * Same syntax as ImageKit URL-based transformations. + */ + value: string; + } + + export interface GifToVideo { + /** + * Converts an animated GIF into an MP4. + */ + type: 'gif-to-video'; + + /** + * Optional transformation string to apply to the output video. + * **Example**: `q-80` + */ + value?: string; + } + + export interface Thumbnail { + /** + * Generates a thumbnail image. + */ + type: 'thumbnail'; + + /** + * Optional transformation string. + * **Example**: `w-150,h-150` + */ + value?: string; + } + + export interface Abs { + /** + * Streaming protocol to use (`hls` or `dash`). + */ + protocol: 'hls' | 'dash'; + + /** + * Adaptive Bitrate Streaming (ABS) setup. + */ + type: 'abs'; + + /** + * List of different representations you want to create separated by an underscore. + */ + value: string; + } + } +} + +export declare namespace Files { + export { type FileUploadResponse as FileUploadResponse, type FileUploadParams as FileUploadParams }; +} diff --git a/src/resources/beta/v2/index.ts b/src/resources/beta/v2/index.ts new file mode 100644 index 00000000..bffa32ec --- /dev/null +++ b/src/resources/beta/v2/index.ts @@ -0,0 +1,4 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { Files, type FileUploadResponse, type FileUploadParams } from './files'; +export { V2 } from './v2'; diff --git a/src/resources/beta/v2/v2.ts b/src/resources/beta/v2/v2.ts new file mode 100644 index 00000000..3a047920 --- /dev/null +++ b/src/resources/beta/v2/v2.ts @@ -0,0 +1,19 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../../core/resource'; +import * as FilesAPI from './files'; +import { FileUploadParams, FileUploadResponse, Files } from './files'; + +export class V2 extends APIResource { + files: FilesAPI.Files = new FilesAPI.Files(this._client); +} + +V2.Files = Files; + +export declare namespace V2 { + export { + Files as Files, + type FileUploadResponse as FileUploadResponse, + type FileUploadParams as FileUploadParams, + }; +} diff --git a/src/resources/cache.ts b/src/resources/cache.ts new file mode 100644 index 00000000..c011d115 --- /dev/null +++ b/src/resources/cache.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './cache/index'; diff --git a/src/resources/cache/cache.ts b/src/resources/cache/cache.ts new file mode 100644 index 00000000..b959081c --- /dev/null +++ b/src/resources/cache/cache.ts @@ -0,0 +1,25 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as InvalidationAPI from './invalidation'; +import { + Invalidation, + InvalidationCreateParams, + InvalidationCreateResponse, + InvalidationGetResponse, +} from './invalidation'; + +export class Cache extends APIResource { + invalidation: InvalidationAPI.Invalidation = new InvalidationAPI.Invalidation(this._client); +} + +Cache.Invalidation = Invalidation; + +export declare namespace Cache { + export { + Invalidation as Invalidation, + type InvalidationCreateResponse as InvalidationCreateResponse, + type InvalidationGetResponse as InvalidationGetResponse, + type InvalidationCreateParams as InvalidationCreateParams, + }; +} diff --git a/src/resources/cache/index.ts b/src/resources/cache/index.ts new file mode 100644 index 00000000..3c9f4fc7 --- /dev/null +++ b/src/resources/cache/index.ts @@ -0,0 +1,9 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { Cache } from './cache'; +export { + Invalidation, + type InvalidationCreateResponse, + type InvalidationGetResponse, + type InvalidationCreateParams, +} from './invalidation'; diff --git a/src/resources/cache/invalidation.ts b/src/resources/cache/invalidation.ts new file mode 100644 index 00000000..d06b2701 --- /dev/null +++ b/src/resources/cache/invalidation.ts @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Invalidation extends APIResource { + /** + * This API will purge CDN cache and ImageKit.io's internal cache for a file. Note: + * Purge cache is an asynchronous process and it may take some time to reflect the + * changes. + * + * @example + * ```ts + * const invalidation = await client.cache.invalidation.create( + * { + * url: 'https://ik.imagekit.io/your_imagekit_id/default-image.jpg', + * }, + * ); + * ``` + */ + create(body: InvalidationCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/purge', { body, ...options }); + } + + /** + * This API returns the status of a purge cache request. + * + * @example + * ```ts + * const invalidation = await client.cache.invalidation.get( + * 'requestId', + * ); + * ``` + */ + get(requestID: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/files/purge/${requestID}`, options); + } +} + +export interface InvalidationCreateResponse { + /** + * Unique identifier of the purge request. This can be used to check the status of + * the purge request. + */ + requestId?: string; +} + +export interface InvalidationGetResponse { + /** + * Status of the purge request. + */ + status?: 'Pending' | 'Completed'; +} + +export interface InvalidationCreateParams { + /** + * The full URL of the file to be purged. + */ + url: string; +} + +export declare namespace Invalidation { + export { + type InvalidationCreateResponse as InvalidationCreateResponse, + type InvalidationGetResponse as InvalidationGetResponse, + type InvalidationCreateParams as InvalidationCreateParams, + }; +} diff --git a/src/resources/custom-metadata-fields.ts b/src/resources/custom-metadata-fields.ts new file mode 100644 index 00000000..10a917e4 --- /dev/null +++ b/src/resources/custom-metadata-fields.ts @@ -0,0 +1,337 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import { APIPromise } from '../core/api-promise'; +import { RequestOptions } from '../internal/request-options'; +import { path } from '../internal/utils/path'; + +export class CustomMetadataFields extends APIResource { + /** + * This API creates a new custom metadata field. Once a custom metadata field is + * created either through this API or using the dashboard UI, its value can be set + * on the assets. The value of a field for an asset can be set using the media + * library UI or programmatically through upload or update assets API. + * + * @example + * ```ts + * const customMetadataField = + * await client.customMetadataFields.create({ + * label: 'price', + * name: 'price', + * schema: { + * type: 'Number', + * minValue: 1000, + * maxValue: 3000, + * }, + * }); + * ``` + */ + create(body: CustomMetadataFieldCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/customMetadataFields', { body, ...options }); + } + + /** + * This API updates the label or schema of an existing custom metadata field. + * + * @example + * ```ts + * const customMetadataField = + * await client.customMetadataFields.update('id', { + * label: 'price', + * schema: { + * type: 'Number', + * minValue: 1000, + * maxValue: 3000, + * }, + * }); + * ``` + */ + update( + id: string, + body: CustomMetadataFieldUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.patch(path`/v1/customMetadataFields/${id}`, { body, ...options }); + } + + /** + * This API returns the array of created custom metadata field objects. By default + * the API returns only non deleted field objects, but you can include deleted + * fields in the API response. + * + * @example + * ```ts + * const customMetadataFields = + * await client.customMetadataFields.list(); + * ``` + */ + list( + query: CustomMetadataFieldListParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.get('/v1/customMetadataFields', { query, ...options }); + } + + /** + * This API deletes a custom metadata field. Even after deleting a custom metadata + * field, you cannot create any new custom metadata field with the same name. + * + * @example + * ```ts + * const customMetadataField = + * await client.customMetadataFields.delete('id'); + * ``` + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v1/customMetadataFields/${id}`, options); + } +} + +/** + * Object containing details of a custom metadata field. + */ +export interface CustomMetadataField { + /** + * Unique identifier for the custom metadata field. Use this to update the field. + */ + id: string; + + /** + * Human readable name of the custom metadata field. This name is displayed as form + * field label to the users while setting field value on the asset in the media + * library UI. + */ + label: string; + + /** + * API name of the custom metadata field. This becomes the key while setting + * `customMetadata` (key-value object) for an asset using upload or update API. + */ + name: string; + + /** + * An object that describes the rules for the custom metadata field value. + */ + schema: CustomMetadataField.Schema; +} + +export namespace CustomMetadataField { + /** + * An object that describes the rules for the custom metadata field value. + */ + export interface Schema { + /** + * Type of the custom metadata field. + */ + type: 'Text' | 'Textarea' | 'Number' | 'Date' | 'Boolean' | 'SingleSelect' | 'MultiSelect'; + + /** + * The default value for this custom metadata field. Date type of default value + * depends on the field type. + */ + defaultValue?: string | number | boolean | Array; + + /** + * Specifies if the this custom metadata field is required or not. + */ + isValueRequired?: boolean; + + /** + * Maximum length of string. Only set if `type` is set to `Text` or `Textarea`. + */ + maxLength?: number; + + /** + * Maximum value of the field. Only set if field type is `Date` or `Number`. For + * `Date` type field, the value will be in ISO8601 string format. For `Number` type + * field, it will be a numeric value. + */ + maxValue?: string | number; + + /** + * Minimum length of string. Only set if `type` is set to `Text` or `Textarea`. + */ + minLength?: number; + + /** + * Minimum value of the field. Only set if field type is `Date` or `Number`. For + * `Date` type field, the value will be in ISO8601 string format. For `Number` type + * field, it will be a numeric value. + */ + minValue?: string | number; + + /** + * An array of allowed values when field type is `SingleSelect` or `MultiSelect`. + */ + selectOptions?: Array; + } +} + +export type CustomMetadataFieldListResponse = Array; + +export interface CustomMetadataFieldDeleteResponse {} + +export interface CustomMetadataFieldCreateParams { + /** + * Human readable name of the custom metadata field. This should be unique across + * all non deleted custom metadata fields. This name is displayed as form field + * label to the users while setting field value on an asset in the media library + * UI. + */ + label: string; + + /** + * API name of the custom metadata field. This should be unique across all + * (including deleted) custom metadata fields. + */ + name: string; + + schema: CustomMetadataFieldCreateParams.Schema; +} + +export namespace CustomMetadataFieldCreateParams { + export interface Schema { + /** + * Type of the custom metadata field. + */ + type: 'Text' | 'Textarea' | 'Number' | 'Date' | 'Boolean' | 'SingleSelect' | 'MultiSelect'; + + /** + * The default value for this custom metadata field. This property is only required + * if `isValueRequired` property is set to `true`. The value should match the + * `type` of custom metadata field. + */ + defaultValue?: string | number | boolean | Array; + + /** + * Sets this custom metadata field as required. Setting custom metadata fields on + * an asset will throw error if the value for all required fields are not present + * in upload or update asset API request body. + */ + isValueRequired?: boolean; + + /** + * Maximum length of string. Only set this property if `type` is set to `Text` or + * `Textarea`. + */ + maxLength?: number; + + /** + * Maximum value of the field. Only set this property if field type is `Date` or + * `Number`. For `Date` type field, set the minimum date in ISO8601 string format. + * For `Number` type field, set the minimum numeric value. + */ + maxValue?: string | number; + + /** + * Minimum length of string. Only set this property if `type` is set to `Text` or + * `Textarea`. + */ + minLength?: number; + + /** + * Minimum value of the field. Only set this property if field type is `Date` or + * `Number`. For `Date` type field, set the minimum date in ISO8601 string format. + * For `Number` type field, set the minimum numeric value. + */ + minValue?: string | number; + + /** + * An array of allowed values. This property is only required if `type` property is + * set to `SingleSelect` or `MultiSelect`. + */ + selectOptions?: Array; + } +} + +export interface CustomMetadataFieldUpdateParams { + /** + * Human readable name of the custom metadata field. This should be unique across + * all non deleted custom metadata fields. This name is displayed as form field + * label to the users while setting field value on an asset in the media library + * UI. This parameter is required if `schema` is not provided. + */ + label?: string; + + /** + * An object that describes the rules for the custom metadata key. This parameter + * is required if `label` is not provided. Note: `type` cannot be updated and will + * be ignored if sent with the `schema`. The schema will be validated as per the + * existing `type`. + */ + schema?: CustomMetadataFieldUpdateParams.Schema; +} + +export namespace CustomMetadataFieldUpdateParams { + /** + * An object that describes the rules for the custom metadata key. This parameter + * is required if `label` is not provided. Note: `type` cannot be updated and will + * be ignored if sent with the `schema`. The schema will be validated as per the + * existing `type`. + */ + export interface Schema { + /** + * The default value for this custom metadata field. This property is only required + * if `isValueRequired` property is set to `true`. The value should match the + * `type` of custom metadata field. + */ + defaultValue?: string | number | boolean | Array; + + /** + * Sets this custom metadata field as required. Setting custom metadata fields on + * an asset will throw error if the value for all required fields are not present + * in upload or update asset API request body. + */ + isValueRequired?: boolean; + + /** + * Maximum length of string. Only set this property if `type` is set to `Text` or + * `Textarea`. + */ + maxLength?: number; + + /** + * Maximum value of the field. Only set this property if field type is `Date` or + * `Number`. For `Date` type field, set the minimum date in ISO8601 string format. + * For `Number` type field, set the minimum numeric value. + */ + maxValue?: string | number; + + /** + * Minimum length of string. Only set this property if `type` is set to `Text` or + * `Textarea`. + */ + minLength?: number; + + /** + * Minimum value of the field. Only set this property if field type is `Date` or + * `Number`. For `Date` type field, set the minimum date in ISO8601 string format. + * For `Number` type field, set the minimum numeric value. + */ + minValue?: string | number; + + /** + * An array of allowed values. This property is only required if `type` property is + * set to `SingleSelect` or `MultiSelect`. + */ + selectOptions?: Array; + } +} + +export interface CustomMetadataFieldListParams { + /** + * Set it to `true` to include deleted field objects in the API response. + */ + includeDeleted?: boolean; +} + +export declare namespace CustomMetadataFields { + export { + type CustomMetadataField as CustomMetadataField, + type CustomMetadataFieldListResponse as CustomMetadataFieldListResponse, + type CustomMetadataFieldDeleteResponse as CustomMetadataFieldDeleteResponse, + type CustomMetadataFieldCreateParams as CustomMetadataFieldCreateParams, + type CustomMetadataFieldUpdateParams as CustomMetadataFieldUpdateParams, + type CustomMetadataFieldListParams as CustomMetadataFieldListParams, + }; +} diff --git a/src/resources/files.ts b/src/resources/files.ts new file mode 100644 index 00000000..46a5299c --- /dev/null +++ b/src/resources/files.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './files/index'; diff --git a/src/resources/files/bulk.ts b/src/resources/files/bulk.ts new file mode 100644 index 00000000..35dec26f --- /dev/null +++ b/src/resources/files/bulk.ts @@ -0,0 +1,171 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Bulk extends APIResource { + /** + * This API deletes multiple files and all their file versions permanently. + * + * Note: If a file or specific transformation has been requested in the past, then + * the response is cached. Deleting a file does not purge the cache. You can purge + * the cache using purge cache API. + * + * A maximum of 100 files can be deleted at a time. + * + * @example + * ```ts + * const bulk = await client.files.bulk.delete({ + * fileIds: [ + * '598821f949c0a938d57563bd', + * '598821f949c0a938d57563be', + * ], + * }); + * ``` + */ + delete(body: BulkDeleteParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/batch/deleteByFileIds', { body, ...options }); + } + + /** + * This API adds tags to multiple files in bulk. A maximum of 50 files can be + * specified at a time. + * + * @example + * ```ts + * const response = await client.files.bulk.addTags({ + * fileIds: [ + * '598821f949c0a938d57563bd', + * '598821f949c0a938d57563be', + * ], + * tags: ['t-shirt', 'round-neck', 'sale2019'], + * }); + * ``` + */ + addTags(body: BulkAddTagsParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/addTags', { body, ...options }); + } + + /** + * This API removes AITags from multiple files in bulk. A maximum of 50 files can + * be specified at a time. + * + * @example + * ```ts + * const response = await client.files.bulk.removeAITags({ + * AITags: ['t-shirt', 'round-neck', 'sale2019'], + * fileIds: [ + * '598821f949c0a938d57563bd', + * '598821f949c0a938d57563be', + * ], + * }); + * ``` + */ + removeAITags(body: BulkRemoveAITagsParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/removeAITags', { body, ...options }); + } + + /** + * This API removes tags from multiple files in bulk. A maximum of 50 files can be + * specified at a time. + * + * @example + * ```ts + * const response = await client.files.bulk.removeTags({ + * fileIds: [ + * '598821f949c0a938d57563bd', + * '598821f949c0a938d57563be', + * ], + * tags: ['t-shirt', 'round-neck', 'sale2019'], + * }); + * ``` + */ + removeTags(body: BulkRemoveTagsParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/removeTags', { body, ...options }); + } +} + +export interface BulkDeleteResponse { + /** + * An array of fileIds that were successfully deleted. + */ + successfullyDeletedFileIds?: Array; +} + +export interface BulkAddTagsResponse { + /** + * An array of fileIds that in which tags were successfully added. + */ + successfullyUpdatedFileIds?: Array; +} + +export interface BulkRemoveAITagsResponse { + /** + * An array of fileIds that in which AITags were successfully removed. + */ + successfullyUpdatedFileIds?: Array; +} + +export interface BulkRemoveTagsResponse { + /** + * An array of fileIds that in which tags were successfully removed. + */ + successfullyUpdatedFileIds?: Array; +} + +export interface BulkDeleteParams { + /** + * An array of fileIds which you want to delete. + */ + fileIds: Array; +} + +export interface BulkAddTagsParams { + /** + * An array of fileIds to which you want to add tags. + */ + fileIds: Array; + + /** + * An array of tags that you want to add to the files. + */ + tags: Array; +} + +export interface BulkRemoveAITagsParams { + /** + * An array of AITags that you want to remove from the files. + */ + AITags: Array; + + /** + * An array of fileIds from which you want to remove AITags. + */ + fileIds: Array; +} + +export interface BulkRemoveTagsParams { + /** + * An array of fileIds from which you want to remove tags. + */ + fileIds: Array; + + /** + * An array of tags that you want to remove from the files. + */ + tags: Array; +} + +export declare namespace Bulk { + export { + type BulkDeleteResponse as BulkDeleteResponse, + type BulkAddTagsResponse as BulkAddTagsResponse, + type BulkRemoveAITagsResponse as BulkRemoveAITagsResponse, + type BulkRemoveTagsResponse as BulkRemoveTagsResponse, + type BulkDeleteParams as BulkDeleteParams, + type BulkAddTagsParams as BulkAddTagsParams, + type BulkRemoveAITagsParams as BulkRemoveAITagsParams, + type BulkRemoveTagsParams as BulkRemoveTagsParams, + }; +} diff --git a/src/resources/files/files.ts b/src/resources/files/files.ts new file mode 100644 index 00000000..0f4fd927 --- /dev/null +++ b/src/resources/files/files.ts @@ -0,0 +1,1430 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as Shared from '../shared'; +import * as BulkAPI from './bulk'; +import { + Bulk, + BulkAddTagsParams, + BulkAddTagsResponse, + BulkDeleteParams, + BulkDeleteResponse, + BulkRemoveAITagsParams, + BulkRemoveAITagsResponse, + BulkRemoveTagsParams, + BulkRemoveTagsResponse, +} from './bulk'; +import * as MetadataAPI from './metadata'; +import { MetadataGetFromURLParams } from './metadata'; +import * as VersionsAPI from './versions'; +import { + VersionDeleteParams, + VersionDeleteResponse, + VersionGetParams, + VersionListResponse, + VersionRestoreParams, + Versions, +} from './versions'; +import { APIPromise } from '../../core/api-promise'; +import { type Uploadable } from '../../core/uploads'; +import { buildHeaders } from '../../internal/headers'; +import { RequestOptions } from '../../internal/request-options'; +import { multipartFormRequestOptions } from '../../internal/uploads'; +import { path } from '../../internal/utils/path'; +import { serializeUploadOptions } from '../../lib/serialization-utils'; + +export class Files extends APIResource { + bulk: BulkAPI.Bulk = new BulkAPI.Bulk(this._client); + versions: VersionsAPI.Versions = new VersionsAPI.Versions(this._client); + metadata: MetadataAPI.Metadata = new MetadataAPI.Metadata(this._client); + + /** + * This API updates the details or attributes of the current version of the file. + * You can update `tags`, `customCoordinates`, `customMetadata`, publication + * status, remove existing `AITags` and apply extensions using this API. + * + * @example + * ```ts + * const file = await client.files.update('fileId'); + * ``` + */ + update(fileID: string, body: FileUpdateParams, options?: RequestOptions): APIPromise { + return this._client.patch(path`/v1/files/${fileID}/details`, { body, ...options }); + } + + /** + * This API deletes the file and all its file versions permanently. + * + * Note: If a file or specific transformation has been requested in the past, then + * the response is cached. Deleting a file does not purge the cache. You can purge + * the cache using purge cache API. + * + * @example + * ```ts + * await client.files.delete('fileId'); + * ``` + */ + delete(fileID: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v1/files/${fileID}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } + + /** + * This will copy a file from one folder to another. + * + * Note: If any file at the destination has the same name as the source file, then + * the source file and its versions (if `includeFileVersions` is set to true) will + * be appended to the destination file version history. + * + * @example + * ```ts + * const response = await client.files.copy({ + * destinationPath: '/folder/to/copy/into/', + * sourceFilePath: '/path/to/file.jpg', + * }); + * ``` + */ + copy(body: FileCopyParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/copy', { body, ...options }); + } + + /** + * This API returns an object with details or attributes about the current version + * of the file. + * + * @example + * ```ts + * const file = await client.files.get('fileId'); + * ``` + */ + get(fileID: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/files/${fileID}/details`, options); + } + + /** + * This will move a file and all its versions from one folder to another. + * + * Note: If any file at the destination has the same name as the source file, then + * the source file and its versions will be appended to the destination file. + * + * @example + * ```ts + * const response = await client.files.move({ + * destinationPath: '/folder/to/move/into/', + * sourceFilePath: '/path/to/file.jpg', + * }); + * ``` + */ + move(body: FileMoveParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/files/move', { body, ...options }); + } + + /** + * You can rename an already existing file in the media library using rename file + * API. This operation would rename all file versions of the file. + * + * Note: The old URLs will stop working. The file/file version URLs cached on CDN + * will continue to work unless a purge is requested. + * + * @example + * ```ts + * const response = await client.files.rename({ + * filePath: '/path/to/file.jpg', + * newFileName: 'newFileName.jpg', + * }); + * ``` + */ + rename(body: FileRenameParams, options?: RequestOptions): APIPromise { + return this._client.put('/v1/files/rename', { body, ...options }); + } + + /** + * ImageKit.io allows you to upload files directly from both the server and client + * sides. For server-side uploads, private API key authentication is used. For + * client-side uploads, generate a one-time `token`, `signature`, and `expire` from + * your secure backend using private API. + * [Learn more](/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload) + * about how to implement client-side file upload. + * + * The [V2 API](/docs/api-reference/upload-file/upload-file-v2) enhances security + * by verifying the entire payload using JWT. + * + * **File size limit** \ + * On the free plan, the maximum upload file sizes are 20MB for images, audio, and raw + * files and 100MB for videos. On the paid plan, these limits increase to 40MB for images, + * audio, and raw files and 2GB for videos. These limits can be further increased with + * higher-tier plans. + * + * **Version limit** \ + * A file can have a maximum of 100 versions. + * + * **Demo applications** + * + * - A full-fledged + * [upload widget using Uppy](https://github.com/imagekit-samples/uppy-uploader), + * supporting file selections from local storage, URL, Dropbox, Google Drive, + * Instagram, and more. + * - [Quick start guides](/docs/quick-start-guides) for various frameworks and + * technologies. + * + * @example + * ```ts + * const response = await client.files.upload({ + * file: fs.createReadStream('path/to/file'), + * fileName: 'fileName', + * }); + * ``` + */ + upload(body: FileUploadParams, options?: RequestOptions): APIPromise { + const serializedBody = serializeUploadOptions(body); + + return this._client.post( + '/api/v1/files/upload', + multipartFormRequestOptions( + { body: serializedBody, defaultBaseURL: 'https://upload.imagekit.io', ...options }, + this._client, + ), + ); + } +} + +/** + * Object containing details of a file or file version. + */ +export interface File { + /** + * An array of tags assigned to the file by auto tagging. + */ + AITags?: Array | null; + + /** + * Date and time when the file was uploaded. The date and time is in ISO8601 + * format. + */ + createdAt?: string; + + /** + * An string with custom coordinates of the file. + */ + customCoordinates?: string | null; + + /** + * An object with custom metadata for the file. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. Can be set by the user or + * the ai-auto-description extension. + */ + description?: string; + + /** + * Unique identifier of the asset. + */ + fileId?: string; + + /** + * Path of the file. This is the path you would use in the URL to access the file. + * For example, if the file is at the root of the media library, the path will be + * `/file.jpg`. If the file is inside a folder named `images`, the path will be + * `/images/file.jpg`. + */ + filePath?: string; + + /** + * Type of the file. Possible values are `image`, `non-image`. + */ + fileType?: string; + + /** + * Specifies if the image has an alpha channel. + */ + hasAlpha?: boolean; + + /** + * Height of the file. + */ + height?: number; + + /** + * Specifies if the file is private or not. + */ + isPrivateFile?: boolean; + + /** + * Specifies if the file is published or not. + */ + isPublished?: boolean; + + /** + * MIME type of the file. + */ + mime?: string; + + /** + * Name of the asset. + */ + name?: string; + + /** + * Size of the file in bytes. + */ + size?: number; + + /** + * An array of tags assigned to the file. Tags are used to search files in the + * media library. + */ + tags?: Array | null; + + /** + * URL of the thumbnail image. This URL is used to access the thumbnail image of + * the file in the media library. + */ + thumbnail?: string; + + /** + * Type of the asset. + */ + type?: 'file' | 'file-version'; + + /** + * Date and time when the file was last updated. The date and time is in ISO8601 + * format. + */ + updatedAt?: string; + + /** + * URL of the file. + */ + url?: string; + + /** + * An object with details of the file version. + */ + versionInfo?: File.VersionInfo; + + /** + * Width of the file. + */ + width?: number; +} + +export namespace File { + export interface AITag { + /** + * Confidence score of the tag. + */ + confidence?: number; + + /** + * Name of the tag. + */ + name?: string; + + /** + * Source of the tag. Possible values are `google-auto-tagging` and + * `aws-auto-tagging`. + */ + source?: string; + } + + /** + * An object with details of the file version. + */ + export interface VersionInfo { + /** + * Unique identifier of the file version. + */ + id?: string; + + /** + * Name of the file version. + */ + name?: string; + } +} + +export interface Folder { + /** + * Date and time when the folder was created. The date and time is in ISO8601 + * format. + */ + createdAt?: string; + + /** + * Unique identifier of the asset. + */ + folderId?: string; + + /** + * Path of the folder. This is the path you would use in the URL to access the + * folder. For example, if the folder is at the root of the media library, the path + * will be /folder. If the folder is inside another folder named images, the path + * will be /images/folder. + */ + folderPath?: string; + + /** + * Name of the asset. + */ + name?: string; + + /** + * Type of the asset. + */ + type?: 'folder'; + + /** + * Date and time when the folder was last updated. The date and time is in ISO8601 + * format. + */ + updatedAt?: string; +} + +/** + * JSON object containing metadata. + */ +export interface Metadata { + /** + * The audio codec used in the video (only for video). + */ + audioCodec?: string; + + /** + * The bit rate of the video in kbps (only for video). + */ + bitRate?: number; + + /** + * The density of the image in DPI. + */ + density?: number; + + /** + * The duration of the video in seconds (only for video). + */ + duration?: number; + + exif?: Metadata.Exif; + + /** + * The format of the file (e.g., 'jpg', 'mp4'). + */ + format?: string; + + /** + * Indicates if the image has a color profile. + */ + hasColorProfile?: boolean; + + /** + * Indicates if the image contains transparent areas. + */ + hasTransparency?: boolean; + + /** + * The height of the image or video in pixels. + */ + height?: number; + + /** + * Perceptual hash of the image. + */ + pHash?: string; + + /** + * The quality indicator of the image. + */ + quality?: number; + + /** + * The file size in bytes. + */ + size?: number; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * The width of the image or video in pixels. + */ + width?: number; +} + +export namespace Metadata { + export interface Exif { + /** + * Object containing Exif details. + */ + exif?: Exif.Exif; + + /** + * Object containing GPS information. + */ + gps?: Exif.Gps; + + /** + * Object containing EXIF image information. + */ + image?: Exif.Image; + + /** + * JSON object. + */ + interoperability?: Exif.Interoperability; + + makernote?: { [key: string]: unknown }; + + /** + * Object containing Thumbnail information. + */ + thumbnail?: Exif.Thumbnail; + } + + export namespace Exif { + /** + * Object containing Exif details. + */ + export interface Exif { + ApertureValue?: number; + + ColorSpace?: number; + + CreateDate?: string; + + CustomRendered?: number; + + DateTimeOriginal?: string; + + ExifImageHeight?: number; + + ExifImageWidth?: number; + + ExifVersion?: string; + + ExposureCompensation?: number; + + ExposureMode?: number; + + ExposureProgram?: number; + + ExposureTime?: number; + + Flash?: number; + + FlashpixVersion?: string; + + FNumber?: number; + + FocalLength?: number; + + FocalPlaneResolutionUnit?: number; + + FocalPlaneXResolution?: number; + + FocalPlaneYResolution?: number; + + InteropOffset?: number; + + ISO?: number; + + MeteringMode?: number; + + SceneCaptureType?: number; + + ShutterSpeedValue?: number; + + SubSecTime?: string; + + WhiteBalance?: number; + } + + /** + * Object containing GPS information. + */ + export interface Gps { + GPSVersionID?: Array; + } + + /** + * Object containing EXIF image information. + */ + export interface Image { + ExifOffset?: number; + + GPSInfo?: number; + + Make?: string; + + Model?: string; + + ModifyDate?: string; + + Orientation?: number; + + ResolutionUnit?: number; + + Software?: string; + + XResolution?: number; + + YCbCrPositioning?: number; + + YResolution?: number; + } + + /** + * JSON object. + */ + export interface Interoperability { + InteropIndex?: string; + + InteropVersion?: string; + } + + /** + * Object containing Thumbnail information. + */ + export interface Thumbnail { + Compression?: number; + + ResolutionUnit?: number; + + ThumbnailLength?: number; + + ThumbnailOffset?: number; + + XResolution?: number; + + YResolution?: number; + } + } +} + +/** + * Schema for update file update request. + */ +export type UpdateFileRequest = + | UpdateFileRequest.UpdateFileDetails + | UpdateFileRequest.ChangePublicationStatus; + +export namespace UpdateFileRequest { + export interface UpdateFileDetails { + /** + * Define an important area in the image in the format `x,y,width,height` e.g. + * `10,10,100,100`. Send `null` to unset this value. + */ + customCoordinates?: string | null; + + /** + * A key-value data to be associated with the asset. To unset a key, send `null` + * value for that key. Before setting any custom metadata on an asset you have to + * create the field using custom metadata fields API. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. + */ + description?: string; + + /** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ + extensions?: Shared.Extensions; + + /** + * An array of AITags associated with the file that you want to remove, e.g. + * `["car", "vehicle", "motorsports"]`. + * + * If you want to remove all AITags associated with the file, send a string - + * "all". + * + * Note: The remove operation for `AITags` executes before any of the `extensions` + * are processed. + */ + removeAITags?: Array | 'all'; + + /** + * An array of tags associated with the file, such as `["tag1", "tag2"]`. Send + * `null` to unset all tags associated with the file. + */ + tags?: Array | null; + + /** + * The final status of extensions after they have completed execution will be + * delivered to this endpoint as a POST request. + * [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + * about the webhook payload structure. + */ + webhookUrl?: string; + } + + export interface ChangePublicationStatus { + /** + * Configure the publication status of a file and its versions. + */ + publish?: ChangePublicationStatus.Publish; + } + + export namespace ChangePublicationStatus { + /** + * Configure the publication status of a file and its versions. + */ + export interface Publish { + /** + * Set to `true` to publish the file. Set to `false` to unpublish the file. + */ + isPublished: boolean; + + /** + * Set to `true` to publish/unpublish all versions of the file. Set to `false` to + * publish/unpublish only the current version of the file. + */ + includeFileVersions?: boolean; + } + } +} + +/** + * Object containing details of a file or file version. + */ +export interface FileUpdateResponse extends File { + extensionStatus?: FileUpdateResponse.ExtensionStatus; +} + +export namespace FileUpdateResponse { + export interface ExtensionStatus { + 'ai-auto-description'?: 'success' | 'pending' | 'failed'; + + 'aws-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'google-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'remove-bg'?: 'success' | 'pending' | 'failed'; + } +} + +export interface FileCopyResponse {} + +export interface FileMoveResponse {} + +export interface FileRenameResponse { + /** + * Unique identifier of the purge request. This can be used to check the status of + * the purge request. + */ + purgeRequestId?: string; +} + +/** + * Object containing details of a successful upload. + */ +export interface FileUploadResponse { + /** + * An array of tags assigned to the uploaded file by auto tagging. + */ + AITags?: Array | null; + + /** + * The audio codec used in the video (only for video). + */ + audioCodec?: string; + + /** + * The bit rate of the video in kbps (only for video). + */ + bitRate?: number; + + /** + * Value of custom coordinates associated with the image in the format + * `x,y,width,height`. If `customCoordinates` are not defined, then it is `null`. + * Send `customCoordinates` in `responseFields` in API request to get the value of + * this field. + */ + customCoordinates?: string | null; + + /** + * A key-value data associated with the asset. Use `responseField` in API request + * to get `customMetadata` in the upload API response. Before setting any custom + * metadata on an asset, you have to create the field using custom metadata fields + * API. Send `customMetadata` in `responseFields` in API request to get the value + * of this field. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. Can be set by the user or + * the ai-auto-description extension. + */ + description?: string; + + /** + * The duration of the video in seconds (only for video). + */ + duration?: number; + + /** + * Consolidated embedded metadata associated with the file. It includes exif, iptc, + * and xmp data. Send `embeddedMetadata` in `responseFields` in API request to get + * embeddedMetadata in the upload API response. + */ + embeddedMetadata?: { [key: string]: unknown }; + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + extensionStatus?: FileUploadResponse.ExtensionStatus; + + /** + * Unique fileId. Store this fileld in your database, as this will be used to + * perform update action on this file. + */ + fileId?: string; + + /** + * The relative path of the file in the media library e.g. + * `/marketing-assets/new-banner.jpg`. + */ + filePath?: string; + + /** + * Type of the uploaded file. Possible values are `image`, `non-image`. + */ + fileType?: string; + + /** + * Height of the image in pixels (Only for images) + */ + height?: number; + + /** + * Is the file marked as private. It can be either `true` or `false`. Send + * `isPrivateFile` in `responseFields` in API request to get the value of this + * field. + */ + isPrivateFile?: boolean; + + /** + * Is the file published or in draft state. It can be either `true` or `false`. + * Send `isPublished` in `responseFields` in API request to get the value of this + * field. + */ + isPublished?: boolean; + + /** + * Legacy metadata. Send `metadata` in `responseFields` in API request to get + * metadata in the upload API response. + */ + metadata?: Metadata; + + /** + * Name of the asset. + */ + name?: string; + + /** + * Size of the image file in Bytes. + */ + size?: number; + + /** + * The array of tags associated with the asset. If no tags are set, it will be + * `null`. Send `tags` in `responseFields` in API request to get the value of this + * field. + */ + tags?: Array | null; + + /** + * In the case of an image, a small thumbnail URL. + */ + thumbnailUrl?: string; + + /** + * A publicly accessible URL of the file. + */ + url?: string; + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + versionInfo?: FileUploadResponse.VersionInfo; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * Width of the image in pixels (Only for Images) + */ + width?: number; +} + +export namespace FileUploadResponse { + export interface AITag { + /** + * Confidence score of the tag. + */ + confidence?: number; + + /** + * Name of the tag. + */ + name?: string; + + /** + * Array of `AITags` associated with the image. If no `AITags` are set, it will be + * null. These tags can be added using the `google-auto-tagging` or + * `aws-auto-tagging` extensions. + */ + source?: string; + } + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + export interface ExtensionStatus { + 'ai-auto-description'?: 'success' | 'pending' | 'failed'; + + 'aws-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'google-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'remove-bg'?: 'success' | 'pending' | 'failed'; + } + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + export interface VersionInfo { + /** + * Unique identifier of the file version. + */ + id?: string; + + /** + * Name of the file version. + */ + name?: string; + } +} + +export type FileUpdateParams = FileUpdateParams.UpdateFileDetails | FileUpdateParams.ChangePublicationStatus; + +export declare namespace FileUpdateParams { + export interface UpdateFileDetails { + /** + * Define an important area in the image in the format `x,y,width,height` e.g. + * `10,10,100,100`. Send `null` to unset this value. + */ + customCoordinates?: string | null; + + /** + * A key-value data to be associated with the asset. To unset a key, send `null` + * value for that key. Before setting any custom metadata on an asset you have to + * create the field using custom metadata fields API. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. + */ + description?: string; + + /** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ + extensions?: Shared.Extensions; + + /** + * An array of AITags associated with the file that you want to remove, e.g. + * `["car", "vehicle", "motorsports"]`. + * + * If you want to remove all AITags associated with the file, send a string - + * "all". + * + * Note: The remove operation for `AITags` executes before any of the `extensions` + * are processed. + */ + removeAITags?: Array | 'all'; + + /** + * An array of tags associated with the file, such as `["tag1", "tag2"]`. Send + * `null` to unset all tags associated with the file. + */ + tags?: Array | null; + + /** + * The final status of extensions after they have completed execution will be + * delivered to this endpoint as a POST request. + * [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + * about the webhook payload structure. + */ + webhookUrl?: string; + } + + export interface ChangePublicationStatus { + /** + * Configure the publication status of a file and its versions. + */ + publish?: ChangePublicationStatus.Publish; + } + + export namespace ChangePublicationStatus { + /** + * Configure the publication status of a file and its versions. + */ + export interface Publish { + /** + * Set to `true` to publish the file. Set to `false` to unpublish the file. + */ + isPublished: boolean; + + /** + * Set to `true` to publish/unpublish all versions of the file. Set to `false` to + * publish/unpublish only the current version of the file. + */ + includeFileVersions?: boolean; + } + } +} + +export interface FileCopyParams { + /** + * Full path to the folder you want to copy the above file into. + */ + destinationPath: string; + + /** + * The full path of the file you want to copy. + */ + sourceFilePath: string; + + /** + * Option to copy all versions of a file. By default, only the current version of + * the file is copied. When set to true, all versions of the file will be copied. + * Default value - `false`. + */ + includeFileVersions?: boolean; +} + +export interface FileMoveParams { + /** + * Full path to the folder you want to move the above file into. + */ + destinationPath: string; + + /** + * The full path of the file you want to move. + */ + sourceFilePath: string; +} + +export interface FileRenameParams { + /** + * The full path of the file you want to rename. + */ + filePath: string; + + /** + * The new name of the file. A filename can contain: + * + * Alphanumeric Characters: `a-z`, `A-Z`, `0-9` (including Unicode letters, marks, + * and numerals in other languages). Special Characters: `.`, `_`, and `-`. + * + * Any other character, including space, will be replaced by `_`. + */ + newFileName: string; + + /** + * Option to purge cache for the old file and its versions' URLs. + * + * When set to true, it will internally issue a purge cache request on CDN to + * remove cached content of old file and its versions. This purge request is + * counted against your monthly purge quota. + * + * Note: If the old file were accessible at + * `https://ik.imagekit.io/demo/old-filename.jpg`, a purge cache request would be + * issued against `https://ik.imagekit.io/demo/old-filename.jpg*` (with a wildcard + * at the end). It will remove the file and its versions' URLs and any + * transformations made using query parameters on this file or its versions. + * However, the cache for file transformations made using path parameters will + * persist. You can purge them using the purge API. For more details, refer to the + * purge API documentation. + * + * Default value - `false` + */ + purgeCache?: boolean; +} + +export interface FileUploadParams { + /** + * The API accepts any of the following: + * + * - **Binary data** – send the raw bytes as `multipart/form-data`. + * - **HTTP / HTTPS URL** – a publicly reachable URL that ImageKit’s servers can + * fetch. + * - **Base64 string** – the file encoded as a Base64 data URI or plain Base64. + * + * When supplying a URL, the server must receive the response headers within 8 + * seconds; otherwise the request fails with 400 Bad Request. + */ + file: Uploadable | string; + + /** + * The name with which the file has to be uploaded. The file name can contain: + * + * - Alphanumeric Characters: `a-z`, `A-Z`, `0-9`. + * - Special Characters: `.`, `-` + * + * Any other character including space will be replaced by `_` + */ + fileName: string; + + /** + * A unique value that the ImageKit.io server will use to recognize and prevent + * subsequent retries for the same request. We suggest using V4 UUIDs, or another + * random string with enough entropy to avoid collisions. This field is only + * required for authentication when uploading a file from the client side. + * + * **Note**: Sending a value that has been used in the past will result in a + * validation error. Even if your previous request resulted in an error, you should + * always send a new value for this field. + */ + token?: string; + + /** + * Server-side checks to run on the asset. Read more about + * [Upload API checks](/docs/api-reference/upload-file/upload-file#upload-api-checks). + */ + checks?: string; + + /** + * Define an important area in the image. This is only relevant for image type + * files. + * + * - To be passed as a string with the x and y coordinates of the top-left corner, + * and width and height of the area of interest in the format `x,y,width,height`. + * For example - `10,10,100,100` + * - Can be used with fo-customtransformation. + * - If this field is not specified and the file is overwritten, then + * customCoordinates will be removed. + */ + customCoordinates?: string; + + /** + * JSON key-value pairs to associate with the asset. Create the custom metadata + * fields before setting these values. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. + */ + description?: string; + + /** + * The time until your signature is valid. It must be a + * [Unix time](https://en.wikipedia.org/wiki/Unix_time) in less than 1 hour into + * the future. It should be in seconds. This field is only required for + * authentication when uploading a file from the client side. + */ + expire?: number; + + /** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ + extensions?: Shared.Extensions; + + /** + * The folder path in which the image has to be uploaded. If the folder(s) didn't + * exist before, a new folder(s) is created. + * + * The folder name can contain: + * + * - Alphanumeric Characters: `a-z` , `A-Z` , `0-9` + * - Special Characters: `/` , `_` , `-` + * + * Using multiple `/` creates a nested folder. + */ + folder?: string; + + /** + * Whether to mark the file as private or not. + * + * If `true`, the file is marked as private and is accessible only using named + * transformation or signed URL. + */ + isPrivateFile?: boolean; + + /** + * Whether to upload file as published or not. + * + * If `false`, the file is marked as unpublished, which restricts access to the + * file only via the media library. Files in draft or unpublished state can only be + * publicly accessed after being published. + * + * The option to upload in draft state is only available in custom enterprise + * pricing plans. + */ + isPublished?: boolean; + + /** + * If set to `true` and a file already exists at the exact location, its AITags + * will be removed. Set `overwriteAITags` to `false` to preserve AITags. + */ + overwriteAITags?: boolean; + + /** + * If the request does not have `customMetadata`, and a file already exists at the + * exact location, existing customMetadata will be removed. + */ + overwriteCustomMetadata?: boolean; + + /** + * If `false` and `useUniqueFileName` is also `false`, and a file already exists at + * the exact location, upload API will return an error immediately. + */ + overwriteFile?: boolean; + + /** + * If the request does not have `tags`, and a file already exists at the exact + * location, existing tags will be removed. + */ + overwriteTags?: boolean; + + /** + * Your ImageKit.io public key. This field is only required for authentication when + * uploading a file from the client side. + */ + publicKey?: string; + + /** + * Array of response field keys to include in the API response body. + */ + responseFields?: Array< + | 'tags' + | 'customCoordinates' + | 'isPrivateFile' + | 'embeddedMetadata' + | 'isPublished' + | 'customMetadata' + | 'metadata' + >; + + /** + * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a + * key. Learn how to create a signature on the page below. This should be in + * lowercase. + * + * Signature must be calculated on the server-side. This field is only required for + * authentication when uploading a file from the client side. + */ + signature?: string; + + /** + * Set the tags while uploading the file. Provide an array of tag strings (e.g. + * `["tag1", "tag2", "tag3"]`). The combined length of all tag characters must not + * exceed 500, and the `%` character is not allowed. If this field is not specified + * and the file is overwritten, the existing tags will be removed. + */ + tags?: Array; + + /** + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. + */ + transformation?: FileUploadParams.Transformation; + + /** + * Whether to use a unique filename for this file or not. + * + * If `true`, ImageKit.io will add a unique suffix to the filename parameter to get + * a unique filename. + * + * If `false`, then the image is uploaded with the provided filename parameter, and + * any existing file with the same name is replaced. + */ + useUniqueFileName?: boolean; + + /** + * The final status of extensions after they have completed execution will be + * delivered to this endpoint as a POST request. + * [Learn more](/docs/api-reference/digital-asset-management-dam/managing-assets/update-file-details#webhook-payload-structure) + * about the webhook payload structure. + */ + webhookUrl?: string; +} + +export namespace FileUploadParams { + /** + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. + */ + export interface Transformation { + /** + * List of transformations to apply _after_ the file is uploaded. + * Each item must match one of the following types: `transformation`, + * `gif-to-video`, `thumbnail`, `abs`. + */ + post?: Array< + | Transformation.Transformation + | Transformation.GifToVideo + | Transformation.Thumbnail + | Transformation.Abs + >; + + /** + * Transformation string to apply before uploading the file to the Media Library. + * Useful for optimizing files at ingestion. + */ + pre?: string; + } + + export namespace Transformation { + export interface Transformation { + /** + * Transformation type. + */ + type: 'transformation'; + + /** + * Transformation string (e.g. `w-200,h-200`). + * Same syntax as ImageKit URL-based transformations. + */ + value: string; + } + + export interface GifToVideo { + /** + * Converts an animated GIF into an MP4. + */ + type: 'gif-to-video'; + + /** + * Optional transformation string to apply to the output video. + * **Example**: `q-80` + */ + value?: string; + } + + export interface Thumbnail { + /** + * Generates a thumbnail image. + */ + type: 'thumbnail'; + + /** + * Optional transformation string. + * **Example**: `w-150,h-150` + */ + value?: string; + } + + export interface Abs { + /** + * Streaming protocol to use (`hls` or `dash`). + */ + protocol: 'hls' | 'dash'; + + /** + * Adaptive Bitrate Streaming (ABS) setup. + */ + type: 'abs'; + + /** + * List of different representations you want to create separated by an underscore. + */ + value: string; + } + } +} + +Files.Bulk = Bulk; +Files.Versions = Versions; + +export declare namespace Files { + export { + type File as File, + type Folder as Folder, + type Metadata as Metadata, + type UpdateFileRequest as UpdateFileRequest, + type FileUpdateResponse as FileUpdateResponse, + type FileCopyResponse as FileCopyResponse, + type FileMoveResponse as FileMoveResponse, + type FileRenameResponse as FileRenameResponse, + type FileUploadResponse as FileUploadResponse, + type FileUpdateParams as FileUpdateParams, + type FileCopyParams as FileCopyParams, + type FileMoveParams as FileMoveParams, + type FileRenameParams as FileRenameParams, + type FileUploadParams as FileUploadParams, + }; + + export { + Bulk as Bulk, + type BulkDeleteResponse as BulkDeleteResponse, + type BulkAddTagsResponse as BulkAddTagsResponse, + type BulkRemoveAITagsResponse as BulkRemoveAITagsResponse, + type BulkRemoveTagsResponse as BulkRemoveTagsResponse, + type BulkDeleteParams as BulkDeleteParams, + type BulkAddTagsParams as BulkAddTagsParams, + type BulkRemoveAITagsParams as BulkRemoveAITagsParams, + type BulkRemoveTagsParams as BulkRemoveTagsParams, + }; + + export { + Versions as Versions, + type VersionListResponse as VersionListResponse, + type VersionDeleteResponse as VersionDeleteResponse, + type VersionDeleteParams as VersionDeleteParams, + type VersionGetParams as VersionGetParams, + type VersionRestoreParams as VersionRestoreParams, + }; + + export { type MetadataGetFromURLParams as MetadataGetFromURLParams }; +} diff --git a/src/resources/files/index.ts b/src/resources/files/index.ts new file mode 100644 index 00000000..de6d1ad7 --- /dev/null +++ b/src/resources/files/index.ts @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + Bulk, + type BulkDeleteResponse, + type BulkAddTagsResponse, + type BulkRemoveAITagsResponse, + type BulkRemoveTagsResponse, + type BulkDeleteParams, + type BulkAddTagsParams, + type BulkRemoveAITagsParams, + type BulkRemoveTagsParams, +} from './bulk'; +export { + Files, + type File, + type Folder, + type Metadata, + type UpdateFileRequest, + type FileUpdateResponse, + type FileCopyResponse, + type FileMoveResponse, + type FileRenameResponse, + type FileUploadResponse, + type FileUpdateParams, + type FileCopyParams, + type FileMoveParams, + type FileRenameParams, + type FileUploadParams, +} from './files'; +export { + Versions, + type VersionListResponse, + type VersionDeleteResponse, + type VersionDeleteParams, + type VersionGetParams, + type VersionRestoreParams, +} from './versions'; +export { type MetadataGetFromURLParams } from './metadata'; diff --git a/src/resources/files/metadata.ts b/src/resources/files/metadata.ts new file mode 100644 index 00000000..18416407 --- /dev/null +++ b/src/resources/files/metadata.ts @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as FilesAPI from './files'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Metadata extends APIResource { + /** + * You can programmatically get image EXIF, pHash, and other metadata for uploaded + * files in the ImageKit.io media library using this API. + * + * You can also get the metadata in upload API response by passing `metadata` in + * `responseFields` parameter. + * + * @example + * ```ts + * const metadata = await client.files.metadata.get('fileId'); + * ``` + */ + get(fileID: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/files/${fileID}/metadata`, options); + } + + /** + * Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL + * using this API. + * + * @example + * ```ts + * const metadata = await client.files.metadata.getFromURL({ + * url: 'https://example.com', + * }); + * ``` + */ + getFromURL(query: MetadataGetFromURLParams, options?: RequestOptions): APIPromise { + return this._client.get('/v1/files/metadata', { query, ...options }); + } +} + +export interface MetadataGetFromURLParams { + /** + * Should be a valid file URL. It should be accessible using your ImageKit.io + * account. + */ + url: string; +} + +export declare namespace Metadata { + export { type MetadataGetFromURLParams as MetadataGetFromURLParams }; +} diff --git a/src/resources/files/versions.ts b/src/resources/files/versions.ts new file mode 100644 index 00000000..4cd160bd --- /dev/null +++ b/src/resources/files/versions.ts @@ -0,0 +1,117 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as FilesAPI from './files'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Versions extends APIResource { + /** + * This API returns details of all versions of a file. + * + * @example + * ```ts + * const files = await client.files.versions.list('fileId'); + * ``` + */ + list(fileID: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/files/${fileID}/versions`, options); + } + + /** + * This API deletes a non-current file version permanently. The API returns an + * empty response. + * + * Note: If you want to delete all versions of a file, use the delete file API. + * + * @example + * ```ts + * const version = await client.files.versions.delete( + * 'versionId', + * { fileId: 'fileId' }, + * ); + * ``` + */ + delete( + versionID: string, + params: VersionDeleteParams, + options?: RequestOptions, + ): APIPromise { + const { fileId } = params; + return this._client.delete(path`/v1/files/${fileId}/versions/${versionID}`, options); + } + + /** + * This API returns an object with details or attributes of a file version. + * + * @example + * ```ts + * const file = await client.files.versions.get('versionId', { + * fileId: 'fileId', + * }); + * ``` + */ + get(versionID: string, params: VersionGetParams, options?: RequestOptions): APIPromise { + const { fileId } = params; + return this._client.get(path`/v1/files/${fileId}/versions/${versionID}`, options); + } + + /** + * This API restores a file version as the current file version. + * + * @example + * ```ts + * const file = await client.files.versions.restore( + * 'versionId', + * { fileId: 'fileId' }, + * ); + * ``` + */ + restore( + versionID: string, + params: VersionRestoreParams, + options?: RequestOptions, + ): APIPromise { + const { fileId } = params; + return this._client.put(path`/v1/files/${fileId}/versions/${versionID}/restore`, options); + } +} + +export type VersionListResponse = Array; + +export interface VersionDeleteResponse {} + +export interface VersionDeleteParams { + /** + * The unique `fileId` of the uploaded file. `fileId` is returned in list and + * search assets API and upload API. + */ + fileId: string; +} + +export interface VersionGetParams { + /** + * The unique `fileId` of the uploaded file. `fileId` is returned in list and + * search assets API and upload API. + */ + fileId: string; +} + +export interface VersionRestoreParams { + /** + * The unique `fileId` of the uploaded file. `fileId` is returned in list and + * search assets API and upload API. + */ + fileId: string; +} + +export declare namespace Versions { + export { + type VersionListResponse as VersionListResponse, + type VersionDeleteResponse as VersionDeleteResponse, + type VersionDeleteParams as VersionDeleteParams, + type VersionGetParams as VersionGetParams, + type VersionRestoreParams as VersionRestoreParams, + }; +} diff --git a/src/resources/folders.ts b/src/resources/folders.ts new file mode 100644 index 00000000..7aaeee34 --- /dev/null +++ b/src/resources/folders.ts @@ -0,0 +1,3 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './folders/index'; diff --git a/src/resources/folders/folders.ts b/src/resources/folders/folders.ts new file mode 100644 index 00000000..916e3042 --- /dev/null +++ b/src/resources/folders/folders.ts @@ -0,0 +1,249 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import * as JobAPI from './job'; +import { Job, JobGetResponse } from './job'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; + +export class Folders extends APIResource { + job: JobAPI.Job = new JobAPI.Job(this._client); + + /** + * This will create a new folder. You can specify the folder name and location of + * the parent folder where this new folder should be created. + * + * @example + * ```ts + * const folder = await client.folders.create({ + * folderName: 'summer', + * parentFolderPath: '/product/images/', + * }); + * ``` + */ + create(body: FolderCreateParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/folder', { body, ...options }); + } + + /** + * This will delete a folder and all its contents permanently. The API returns an + * empty response. + * + * @example + * ```ts + * const folder = await client.folders.delete({ + * folderPath: '/folder/to/delete/', + * }); + * ``` + */ + delete(body: FolderDeleteParams, options?: RequestOptions): APIPromise { + return this._client.delete('/v1/folder', { body, ...options }); + } + + /** + * This will copy one folder into another. The selected folder, its nested folders, + * files, and their versions (in `includeVersions` is set to true) are copied in + * this operation. Note: If any file at the destination has the same name as the + * source file, then the source file and its versions will be appended to the + * destination file version history. + * + * @example + * ```ts + * const response = await client.folders.copy({ + * destinationPath: '/path/of/destination/folder', + * sourceFolderPath: '/path/of/source/folder', + * }); + * ``` + */ + copy(body: FolderCopyParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/bulkJobs/copyFolder', { body, ...options }); + } + + /** + * This will move one folder into another. The selected folder, its nested folders, + * files, and their versions are moved in this operation. Note: If any file at the + * destination has the same name as the source file, then the source file and its + * versions will be appended to the destination file version history. + * + * @example + * ```ts + * const response = await client.folders.move({ + * destinationPath: '/path/of/destination/folder', + * sourceFolderPath: '/path/of/source/folder', + * }); + * ``` + */ + move(body: FolderMoveParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/bulkJobs/moveFolder', { body, ...options }); + } + + /** + * This API allows you to rename an existing folder. The folder and all its nested + * assets and sub-folders will remain unchanged, but their paths will be updated to + * reflect the new folder name. + * + * @example + * ```ts + * const response = await client.folders.rename({ + * folderPath: '/path/of/folder', + * newFolderName: 'new-folder-name', + * }); + * ``` + */ + rename(body: FolderRenameParams, options?: RequestOptions): APIPromise { + return this._client.post('/v1/bulkJobs/renameFolder', { body, ...options }); + } +} + +export interface FolderCreateResponse {} + +export interface FolderDeleteResponse {} + +/** + * Job submitted successfully. A `jobId` will be returned. + */ +export interface FolderCopyResponse { + /** + * Unique identifier of the bulk job. This can be used to check the status of the + * bulk job. + */ + jobId: string; +} + +/** + * Job submitted successfully. A `jobId` will be returned. + */ +export interface FolderMoveResponse { + /** + * Unique identifier of the bulk job. This can be used to check the status of the + * bulk job. + */ + jobId: string; +} + +/** + * Job submitted successfully. A `jobId` will be returned. + */ +export interface FolderRenameResponse { + /** + * Unique identifier of the bulk job. This can be used to check the status of the + * bulk job. + */ + jobId: string; +} + +export interface FolderCreateParams { + /** + * The folder will be created with this name. + * + * All characters except alphabets and numbers (inclusive of unicode letters, + * marks, and numerals in other languages) will be replaced by an underscore i.e. + * `_`. + */ + folderName: string; + + /** + * The folder where the new folder should be created, for root use `/` else the + * path e.g. `containing/folder/`. + * + * Note: If any folder(s) is not present in the parentFolderPath parameter, it will + * be automatically created. For example, if you pass `/product/images/summer`, + * then `product`, `images`, and `summer` folders will be created if they don't + * already exist. + */ + parentFolderPath: string; +} + +export interface FolderDeleteParams { + /** + * Full path to the folder you want to delete. For example `/folder/to/delete/`. + */ + folderPath: string; +} + +export interface FolderCopyParams { + /** + * Full path to the destination folder where you want to copy the source folder + * into. + */ + destinationPath: string; + + /** + * The full path to the source folder you want to copy. + */ + sourceFolderPath: string; + + /** + * Option to copy all versions of files that are nested inside the selected folder. + * By default, only the current version of each file will be copied. When set to + * true, all versions of each file will be copied. Default value - `false`. + */ + includeVersions?: boolean; +} + +export interface FolderMoveParams { + /** + * Full path to the destination folder where you want to move the source folder + * into. + */ + destinationPath: string; + + /** + * The full path to the source folder you want to move. + */ + sourceFolderPath: string; +} + +export interface FolderRenameParams { + /** + * The full path to the folder you want to rename. + */ + folderPath: string; + + /** + * The new name for the folder. + * + * All characters except alphabets and numbers (inclusive of unicode letters, + * marks, and numerals in other languages) and `-` will be replaced by an + * underscore i.e. `_`. + */ + newFolderName: string; + + /** + * Option to purge cache for the old nested files and their versions' URLs. + * + * When set to true, it will internally issue a purge cache request on CDN to + * remove the cached content of the old nested files and their versions. There will + * only be one purge request for all the nested files, which will be counted + * against your monthly purge quota. + * + * Note: A purge cache request will be issued against + * `https://ik.imagekit.io/old/folder/path*` (with a wildcard at the end). This + * will remove all nested files, their versions' URLs, and any transformations made + * using query parameters on these files or their versions. However, the cache for + * file transformations made using path parameters will persist. You can purge them + * using the purge API. For more details, refer to the purge API documentation. + * + * Default value - `false` + */ + purgeCache?: boolean; +} + +Folders.Job = Job; + +export declare namespace Folders { + export { + type FolderCreateResponse as FolderCreateResponse, + type FolderDeleteResponse as FolderDeleteResponse, + type FolderCopyResponse as FolderCopyResponse, + type FolderMoveResponse as FolderMoveResponse, + type FolderRenameResponse as FolderRenameResponse, + type FolderCreateParams as FolderCreateParams, + type FolderDeleteParams as FolderDeleteParams, + type FolderCopyParams as FolderCopyParams, + type FolderMoveParams as FolderMoveParams, + type FolderRenameParams as FolderRenameParams, + }; + + export { Job as Job, type JobGetResponse as JobGetResponse }; +} diff --git a/src/resources/folders/index.ts b/src/resources/folders/index.ts new file mode 100644 index 00000000..7759a7d4 --- /dev/null +++ b/src/resources/folders/index.ts @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export { + Folders, + type FolderCreateResponse, + type FolderDeleteResponse, + type FolderCopyResponse, + type FolderMoveResponse, + type FolderRenameResponse, + type FolderCreateParams, + type FolderDeleteParams, + type FolderCopyParams, + type FolderMoveParams, + type FolderRenameParams, +} from './folders'; +export { Job, type JobGetResponse } from './job'; diff --git a/src/resources/folders/job.ts b/src/resources/folders/job.ts new file mode 100644 index 00000000..a9fc92b6 --- /dev/null +++ b/src/resources/folders/job.ts @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../../core/resource'; +import { APIPromise } from '../../core/api-promise'; +import { RequestOptions } from '../../internal/request-options'; +import { path } from '../../internal/utils/path'; + +export class Job extends APIResource { + /** + * This API returns the status of a bulk job like copy and move folder operations. + * + * @example + * ```ts + * const job = await client.folders.job.get('jobId'); + * ``` + */ + get(jobID: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v1/bulkJobs/${jobID}`, options); + } +} + +export interface JobGetResponse { + /** + * Unique identifier of the bulk job. + */ + jobId?: string; + + /** + * Unique identifier of the purge request. This will be present only if + * `purgeCache` is set to `true` in the rename folder API request. + */ + purgeRequestId?: string; + + /** + * Status of the bulk job. + */ + status?: 'Pending' | 'Completed'; + + /** + * Type of the bulk job. + */ + type?: 'COPY_FOLDER' | 'MOVE_FOLDER' | 'RENAME_FOLDER'; +} + +export declare namespace Job { + export { type JobGetResponse as JobGetResponse }; +} diff --git a/src/resources/helper.ts b/src/resources/helper.ts new file mode 100644 index 00000000..f6e63547 --- /dev/null +++ b/src/resources/helper.ts @@ -0,0 +1,462 @@ +// Helper resource for additional utility functions +// File manually created for helper functions - not generated + +import { APIResource } from '../core/resource'; +import type { ImageKit } from '../client'; +import type { + SrcOptions, + Transformation, + ImageOverlay, + TextOverlay, + VideoOverlay, + SubtitleOverlay, + SolidColorOverlay, +} from './shared'; +import transformationUtils, { safeBtoa } from '../lib/transformation-utils'; +import { createHmacSha1 } from '../lib/crypto-utils'; +import { uuid4 } from '../internal/utils/uuid'; + +const TRANSFORMATION_PARAMETER = 'tr'; +const SIGNATURE_PARAMETER = 'ik-s'; +const TIMESTAMP_PARAMETER = 'ik-t'; +const DEFAULT_TIMESTAMP = 9999999999; +const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$'); +const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$'); + +export class Helper extends APIResource { + constructor(client: ImageKit) { + super(client); + } + + /** + * Builds a source URL with the given options. + * + * @param opts - The options for building the source URL. + * @returns The constructed source URL. + */ + buildSrc(opts: SrcOptions): string { + opts.urlEndpoint = opts.urlEndpoint || ''; + opts.src = opts.src || ''; + opts.transformationPosition = opts.transformationPosition || 'query'; + + if (!opts.src) { + return ''; + } + + const isAbsoluteURL = opts.src.startsWith('http://') || opts.src.startsWith('https://'); + + var urlObj, isSrcParameterUsedForURL, urlEndpointPattern; + + try { + if (!isAbsoluteURL) { + urlEndpointPattern = new URL(opts.urlEndpoint).pathname; + urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ''), opts.src])); + } else { + urlObj = new URL(opts.src!); + isSrcParameterUsedForURL = true; + } + } catch (e) { + return ''; + } + + for (var i in opts.queryParameters) { + urlObj.searchParams.append(i, String(opts.queryParameters[i])); + } + + var transformationString = this.buildTransformationString(opts.transformation); + + if (transformationString && transformationString.length) { + if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { + urlObj.pathname = pathJoin([ + TRANSFORMATION_PARAMETER + transformationUtils.getChainTransformDelimiter() + transformationString, + urlObj.pathname, + ]); + } + } + + if (urlEndpointPattern) { + urlObj.pathname = pathJoin([urlEndpointPattern, urlObj.pathname]); + } else { + urlObj.pathname = pathJoin([urlObj.pathname]); + } + + // First, build the complete URL with transformations + let finalUrl = urlObj.href; + + // Add transformation parameter manually to avoid URL encoding + // URLSearchParams.set() would encode commas and colons in transformation string, + // It would work correctly but not very readable e.g., "w-300,h-400" is better than "w-300%2Ch-400" + if (transformationString && transformationString.length) { + if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { + const separator = urlObj.searchParams.toString() ? '&' : '?'; + finalUrl = `${finalUrl}${separator}${TRANSFORMATION_PARAMETER}=${transformationString}`; + } + } + + // Then sign the URL if needed + if (opts.signed === true || (opts.expiresIn && opts.expiresIn > 0)) { + const expiryTimestamp = getSignatureTimestamp(opts.expiresIn); + + const urlSignature = getSignature({ + privateKey: this._client.privateKey, + url: finalUrl, + urlEndpoint: opts.urlEndpoint, + expiryTimestamp, + }); + + // Add signature parameters to the final URL + // Use URL object to properly determine if we need ? or & separator + const finalUrlObj = new URL(finalUrl); + const hasExistingParams = finalUrlObj.searchParams.toString().length > 0; + const separator = hasExistingParams ? '&' : '?'; + let signedUrl = finalUrl; + + if (expiryTimestamp && expiryTimestamp !== DEFAULT_TIMESTAMP) { + signedUrl += `${separator}${TIMESTAMP_PARAMETER}=${expiryTimestamp}`; + signedUrl += `&${SIGNATURE_PARAMETER}=${urlSignature}`; + } else { + signedUrl += `${separator}${SIGNATURE_PARAMETER}=${urlSignature}`; + } + + return signedUrl; + } + + return finalUrl; + } + + /** + * Builds a transformation string from the given transformations. + * + * @param transformation - The transformations to apply. + * @returns The constructed transformation string. + */ + buildTransformationString(transformation: Transformation[] | undefined): string { + return buildTransformationString(transformation); + } + + /** + * Generates authentication parameters for client-side file uploads using ImageKit's Upload API V1. + * + * This method creates the required authentication signature that allows secure file uploads + * directly from the browser or mobile applications without exposing your private API key. + * The generated parameters include a unique token, expiration timestamp, and HMAC signature. + * + * @param token - Custom token for the upload session. If not provided, a UUID v4 will be generated automatically. + * @param expire - Expiration time in seconds from now. If not provided, defaults to 1800 seconds (30 minutes). + * @returns Authentication parameters object containing: + * - `token`: Unique identifier for this upload session + * - `expire`: Unix timestamp when these parameters expire + * - `signature`: HMAC-SHA1 signature for authenticating the upload + * + * @throws {Error} If the private API key is not configured (should not happen in normal usage) + */ + getAuthenticationParameters(token?: string, expire?: number) { + if (!this._client.privateKey) { + throw new Error('Private API key is required for authentication parameters generation'); + } + + const DEFAULT_TIME_DIFF = 60 * 30; + const defaultExpire = Math.floor(Date.now() / 1000) + DEFAULT_TIME_DIFF; + + const finalToken = token || uuid4(); + const finalExpire = expire || defaultExpire; + + return getAuthenticationParameters(finalToken, finalExpire, this._client.privateKey); + } +} + +const getAuthenticationParameters = function (token: string, expire: number, privateKey: string) { + var authParameters = { + token: token, + expire: expire, + signature: '', + }; + + var signature = createHmacSha1(privateKey, token + expire); + + authParameters.signature = signature; + + return authParameters; +}; + +function removeTrailingSlash(str: string): string { + if (typeof str == 'string' && str[str.length - 1] == '/') { + str = str.substring(0, str.length - 1); + } + return str; +} + +function removeLeadingSlash(str: string): string { + if (typeof str == 'string' && str[0] == '/') { + str = str.slice(1); + } + return str; +} + +function pathJoin(parts: string[], sep?: string): string { + var separator = sep || '/'; + var replace = new RegExp(separator + '{1,}', 'g'); + return parts.join(separator).replace(replace, separator); +} + +function processInputPath(str: string, encoding: string): string { + // Remove leading and trailing slashes + str = removeTrailingSlash(removeLeadingSlash(str)); + if (encoding === 'plain') { + return `i-${str.replace(/\//g, '@@')}`; + } + if (encoding === 'base64') { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } + if (SIMPLE_OVERLAY_PATH_REGEX.test(str)) { + return `i-${str.replace(/\//g, '@@')}`; + } else { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } +} + +function processText(str: string, encoding: TextOverlay['encoding']): string { + if (encoding === 'plain') { + return `i-${encodeURIComponent(str)}`; + } + if (encoding === 'base64') { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } + if (SIMPLE_OVERLAY_TEXT_REGEX.test(str)) { + return `i-${encodeURIComponent(str)}`; + } + return `ie-${encodeURIComponent(safeBtoa(str))}`; +} + +function processOverlay(overlay: Transformation['overlay']): string | undefined { + const entries = []; + + const { type, position = {}, timing = {}, transformation = [] } = overlay || {}; + + if (!type) { + return; + } + + switch (type) { + case 'text': + { + const textOverlay = overlay as TextOverlay; + if (!textOverlay.text) { + return; + } + const encoding = textOverlay.encoding || 'auto'; + + entries.push('l-text'); + entries.push(processText(textOverlay.text, encoding)); + } + break; + case 'image': + entries.push('l-image'); + { + const imageOverlay = overlay as ImageOverlay; + const encoding = imageOverlay.encoding || 'auto'; + if (imageOverlay.input) { + entries.push(processInputPath(imageOverlay.input, encoding)); + } else { + return; + } + } + break; + case 'video': + entries.push('l-video'); + { + const videoOverlay = overlay as VideoOverlay; + const encoding = videoOverlay.encoding || 'auto'; + if (videoOverlay.input) { + entries.push(processInputPath(videoOverlay.input, encoding)); + } else { + return; + } + } + break; + case 'subtitle': + entries.push('l-subtitle'); + { + const subtitleOverlay = overlay as SubtitleOverlay; + const encoding = subtitleOverlay.encoding || 'auto'; + if (subtitleOverlay.input) { + entries.push(processInputPath(subtitleOverlay.input, encoding)); + } else { + return; + } + } + break; + case 'solidColor': + entries.push('l-image'); + entries.push(`i-ik_canvas`); + { + const solidColorOverlay = overlay as SolidColorOverlay; + if (solidColorOverlay.color) { + entries.push(`bg-${solidColorOverlay.color}`); + } else { + return; + } + } + break; + } + + const { x, y, focus } = position; + if (x) { + entries.push(`lx-${x}`); + } + if (y) { + entries.push(`ly-${y}`); + } + if (focus) { + entries.push(`lfo-${focus}`); + } + + const { start, end, duration } = timing; + if (start) { + entries.push(`lso-${start}`); + } + if (end) { + entries.push(`leo-${end}`); + } + if (duration) { + entries.push(`ldu-${duration}`); + } + + const transformationString = buildTransformationString(transformation); + + if (transformationString && transformationString.trim() !== '') { + entries.push(transformationString); + } + + entries.push('l-end'); + + return entries.join(transformationUtils.getTransformDelimiter()); +} + +function buildTransformationString(transformation: Transformation[] | undefined): string { + if (!Array.isArray(transformation)) { + return ''; + } + + var parsedTransforms: string[] = []; + for (var i = 0, l = transformation.length; i < l; i++) { + var parsedTransformStep: string[] = []; + const currentTransform = transformation[i]; + if (!currentTransform) continue; + + for (var key in currentTransform) { + let value = currentTransform[key as keyof Transformation]; + if (value === undefined || value === null) { + continue; + } + + if (key === 'overlay' && typeof value === 'object') { + var rawString = processOverlay(value as Transformation['overlay']); + if (rawString && rawString.trim() !== '') { + parsedTransformStep.push(rawString); + } + continue; // Always continue as overlay is processed. + } + + var transformKey = transformationUtils.getTransformKey(key); + if (!transformKey) { + transformKey = key; + } + + if (transformKey === '') { + continue; + } + + if ( + [ + 'e-grayscale', + 'e-contrast', + 'e-removedotbg', + 'e-bgremove', + 'e-upscale', + 'e-retouch', + 'e-genvar', + ].includes(transformKey) + ) { + if (value === true || value === '-' || value === 'true') { + parsedTransformStep.push(transformKey); + } else { + // Any other value means that the effect should not be applied + continue; + } + } else if ( + ['e-sharpen', 'e-shadow', 'e-gradient', 'e-usm', 'e-dropshadow'].includes(transformKey) && + (value.toString().trim() === '' || value === true || value === 'true') + ) { + parsedTransformStep.push(transformKey); + } else if (key === 'raw') { + parsedTransformStep.push(currentTransform[key] as string); + } else { + if (transformKey === 'di' || transformKey === 'ff') { + value = removeTrailingSlash(removeLeadingSlash((value as string) || '')); + value = value.replace(/\//g, '@@'); + } + if (transformKey === 'sr' && Array.isArray(value)) { + value = value.join('_'); + } + // Special case for trim with empty string - should be treated as true + if (transformKey === 't' && value.toString().trim() === '') { + value = 'true'; + } + + parsedTransformStep.push( + [transformKey, value].join(transformationUtils.getTransformKeyValueDelimiter()), + ); + } + } + if (parsedTransformStep.length) { + parsedTransforms.push(parsedTransformStep.join(transformationUtils.getTransformDelimiter())); + } + } + + return parsedTransforms.join(transformationUtils.getChainTransformDelimiter()); +} + +/** + * Calculates the expiry timestamp for URL signing + * + * @param seconds - Number of seconds from now when the URL should expire + * @returns Unix timestamp for expiry, or DEFAULT_TIMESTAMP if invalid/not provided + */ +function getSignatureTimestamp(seconds: number | undefined): number { + if (!seconds || seconds <= 0) return DEFAULT_TIMESTAMP; + + const sec = parseInt(String(seconds), 10); + if (!sec || isNaN(sec)) return DEFAULT_TIMESTAMP; + + const currentTimestamp = Math.floor(new Date().getTime() / 1000); + return currentTimestamp + sec; +} + +/** + * Generates an HMAC-SHA1 signature for URL signing + * + * @param opts - Options containing private key, URL, endpoint, and expiry timestamp + * @returns Hex-encoded signature, or empty string if required params missing + */ +function getSignature(opts: { + privateKey: string; + url: string; + urlEndpoint: string; + expiryTimestamp: number; +}): string { + if (!opts.privateKey || !opts.url || !opts.urlEndpoint) return ''; + + // Create the string to sign: relative path + expiry timestamp + const stringToSign = + opts.url.replace(addTrailingSlash(opts.urlEndpoint), '') + String(opts.expiryTimestamp); + + return createHmacSha1(opts.privateKey, stringToSign); +} + +function addTrailingSlash(str: string): string { + if (typeof str === 'string' && str[str.length - 1] !== '/') { + str = str + '/'; + } + return str; +} diff --git a/src/resources/index.ts b/src/resources/index.ts new file mode 100644 index 00000000..610aa27d --- /dev/null +++ b/src/resources/index.ts @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export * from './shared'; +export { Accounts } from './accounts/accounts'; +export { Assets, type AssetListResponse, type AssetListParams } from './assets'; +export { Beta } from './beta/beta'; +export { Cache } from './cache/cache'; +export { + CustomMetadataFields, + type CustomMetadataField, + type CustomMetadataFieldListResponse, + type CustomMetadataFieldDeleteResponse, + type CustomMetadataFieldCreateParams, + type CustomMetadataFieldUpdateParams, + type CustomMetadataFieldListParams, +} from './custom-metadata-fields'; +export { + Files, + type File, + type Folder, + type Metadata, + type UpdateFileRequest, + type FileUpdateResponse, + type FileCopyResponse, + type FileMoveResponse, + type FileRenameResponse, + type FileUploadResponse, + type FileUpdateParams, + type FileCopyParams, + type FileMoveParams, + type FileRenameParams, + type FileUploadParams, +} from './files/files'; +export { + Folders, + type FolderCreateResponse, + type FolderDeleteResponse, + type FolderCopyResponse, + type FolderMoveResponse, + type FolderRenameResponse, + type FolderCreateParams, + type FolderDeleteParams, + type FolderCopyParams, + type FolderMoveParams, + type FolderRenameParams, +} from './folders/folders'; +export { + Webhooks, + type BaseWebhookEvent, + type UploadPostTransformErrorEvent, + type UploadPostTransformSuccessEvent, + type UploadPreTransformErrorEvent, + type UploadPreTransformSuccessEvent, + type VideoTransformationAcceptedEvent, + type VideoTransformationErrorEvent, + type VideoTransformationReadyEvent, + type UnsafeUnwrapWebhookEvent, + type UnwrapWebhookEvent, +} from './webhooks'; +export { Helper } from './helper'; diff --git a/src/resources/shared.ts b/src/resources/shared.ts new file mode 100644 index 00000000..6d73d695 --- /dev/null +++ b/src/resources/shared.ts @@ -0,0 +1,952 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export interface BaseOverlay { + position?: OverlayPosition; + + timing?: OverlayTiming; +} + +/** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ +export type Extensions = Array< + Extensions.RemoveBg | Extensions.AutoTaggingExtension | Extensions.AIAutoDescription +>; + +export namespace Extensions { + export interface RemoveBg { + /** + * Specifies the background removal extension. + */ + name: 'remove-bg'; + + options?: RemoveBg.Options; + } + + export namespace RemoveBg { + export interface Options { + /** + * Whether to add an artificial shadow to the result. Default is false. Note: + * Adding shadows is currently only supported for car photos. + */ + add_shadow?: boolean; + + /** + * Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or + * color name (e.g., "green"). If this parameter is set, `bg_image_url` must be + * empty. + */ + bg_color?: string; + + /** + * Sets a background image from a URL. If this parameter is set, `bg_color` must be + * empty. + */ + bg_image_url?: string; + + /** + * Allows semi-transparent regions in the result. Default is true. Note: + * Semitransparency is currently only supported for car windows. + */ + semitransparency?: boolean; + } + } + + export interface AutoTaggingExtension { + /** + * Maximum number of tags to attach to the asset. + */ + maxTags: number; + + /** + * Minimum confidence level for tags to be considered valid. + */ + minConfidence: number; + + /** + * Specifies the auto-tagging extension used. + */ + name: 'google-auto-tagging' | 'aws-auto-tagging'; + } + + export interface AIAutoDescription { + /** + * Specifies the auto description extension. + */ + name: 'ai-auto-description'; + } +} + +export interface ImageOverlay extends BaseOverlay { + /** + * Specifies the relative path to the image used as an overlay. + */ + input: string; + + type: 'image'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Array of transformations to be applied to the overlay image. Supported + * transformations depends on the base/parent asset. See overlays on + * [Images](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) + * and + * [Videos](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay). + */ + transformation?: Array; +} + +/** + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + */ +export type Overlay = TextOverlay | ImageOverlay | VideoOverlay | SubtitleOverlay | SolidColorOverlay; + +export interface OverlayPosition { + /** + * Specifies the position of the overlay relative to the parent image or video. + * Maps to `lfo` in the URL. + */ + focus?: + | 'center' + | 'top' + | 'left' + | 'bottom' + | 'right' + | 'top_left' + | 'top_right' + | 'bottom_left' + | 'bottom_right'; + + /** + * Specifies the x-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + x?: number | string; + + /** + * Specifies the y-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + y?: number | string; +} + +export interface OverlayTiming { + /** + * Specifies the duration (in seconds) during which the overlay should appear on + * the base video. Accepts a positive number up to two decimal places (e.g., `20` + * or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `ldu` in the URL. + */ + duration?: number | string; + + /** + * Specifies the end time (in seconds) for when the overlay should disappear from + * the base video. If both end and duration are provided, duration is ignored. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and + * arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies only if + * the base asset is a video. Maps to `leo` in the URL. + */ + end?: number | string; + + /** + * Specifies the start time (in seconds) for when the overlay should appear on the + * base video. Accepts a positive number up to two decimal places (e.g., `20` or + * `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `lso` in the URL. + */ + start?: number | string; +} + +export interface SolidColorOverlay extends BaseOverlay { + /** + * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA + * code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character value + * is provided, the last two characters represent the opacity level (from `00` for + * 0.00 to `99` for 0.99). + */ + color: string; + + type: 'solidColor'; + + /** + * Control width and height of the solid color overlay. Supported transformations + * depend on the base/parent asset. See overlays on + * [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) + * and + * [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). + */ + transformation?: Array; +} + +export interface SolidColorOverlayTransformation { + /** + * Specifies the transparency level of the solid color overlay. Accepts integers + * from `1` to `9`. + */ + alpha?: number; + + /** + * Specifies the background color of the solid color overlay. Accepts an RGB hex + * code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + */ + background?: string; + + /** + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. Only works if the base asset is an + * image. See + * [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + */ + gradient?: true | string; + + /** + * Controls the height of the solid color overlay. Accepts a numeric value or an + * arithmetic expression. Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + height?: number | string; + + /** + * Specifies the corner radius of the solid color overlay. Set to `max` for + * circular or oval shape. See + * [radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max'; + + /** + * Controls the width of the solid color overlay. Accepts a numeric value or an + * arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + width?: number | string; +} + +/** + * Options for generating ImageKit URLs with transformations. See the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ +export interface SrcOptions { + /** + * Accepts a relative or absolute path of the resource. If a relative path is + * provided, it is appended to the `urlEndpoint`. If an absolute path is provided, + * `urlEndpoint` is ignored. + */ + src: string; + + /** + * Get your urlEndpoint from the + * [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + */ + urlEndpoint: string; + + /** + * When you want the signed URL to expire, specified in seconds. If `expiresIn` is + * anything above 0, the URL will always be signed even if `signed` is set to + * false. If not specified and `signed` is `true`, the signed URL will not expire + * (valid indefinitely). + * + * Example: Setting `expiresIn: 3600` will make the URL expire 1 hour from + * generation time. After the expiry time, the signed URL will no longer be valid + * and ImageKit will return a 401 Unauthorized status code. + * + * [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + */ + expiresIn?: number; + + /** + * These are additional query parameters that you want to add to the final URL. + * They can be any query parameters and not necessarily related to ImageKit. This + * is especially useful if you want to add a versioning parameter to your URLs. + */ + queryParameters?: { [key: string]: string }; + + /** + * Whether to sign the URL or not. Set this to `true` if you want to generate a + * signed URL. If `signed` is `true` and `expiresIn` is not specified, the signed + * URL will not expire (valid indefinitely). Note: If `expiresIn` is set to any + * value above 0, the URL will always be signed regardless of this setting. + * [Learn more](https://imagekit.io/docs/media-delivery-basic-security#how-to-generate-signed-urls). + */ + signed?: boolean; + + /** + * An array of objects specifying the transformations to be applied in the URL. If + * more than one transformation is specified, they are applied in the order they + * are specified as chained transformations. See + * [Chained transformations](https://imagekit.io/docs/transformations#chained-transformations). + */ + transformation?: Array; + + /** + * By default, the transformation string is added as a query parameter in the URL, + * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the + * path of the URL, set this to `path`. Learn more in the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ + transformationPosition?: TransformationPosition; +} + +/** + * Available streaming resolutions for + * [adaptive bitrate streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) + */ +export type StreamingResolution = '240' | '360' | '480' | '720' | '1080' | '1440' | '2160'; + +export interface SubtitleOverlay extends BaseOverlay { + /** + * Specifies the relative path to the subtitle file used as an overlay. + */ + input: string; + + type: 'subtitle'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Control styling of the subtitle. See + * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). + */ + transformation?: Array; +} + +/** + * Subtitle styling options. + * [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + * from the docs. + */ +export interface SubtitleOverlayTransformation { + /** + * Specifies the subtitle background color using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + background?: string; + + /** + * Sets the font color of the subtitle text using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + color?: string; + + /** + * Font family for subtitles. Refer to the + * [supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list). + */ + fontFamily?: string; + + /** + * Sets the font outline of the subtitle text. Requires the outline width (an + * integer) and the outline color (as an RGB color code, RGBA color code, or + * standard web color name) separated by an underscore. Example: `fol-2_blue` + * (outline width of 2px and outline color blue), `fol-2_A1CCDD` (outline width of + * 2px and outline color `#A1CCDD`) and `fol-2_A1CCDD50` (outline width of 2px and + * outline color `#A1CCDD` at 50% opacity). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontOutline?: string; + + /** + * Sets the font shadow for the subtitle text. Requires the shadow color (as an RGB + * color code, RGBA color code, or standard web color name) and shadow indent (an + * integer) separated by an underscore. Example: `fsh-blue_2` (shadow color blue, + * indent of 2px), `fsh-A1CCDD_3` (shadow color `#A1CCDD`, indent of 3px), + * `fsh-A1CCDD50_3` (shadow color `#A1CCDD` at 50% opacity, indent of 3px). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontShadow?: string; + + /** + * Sets the font size of subtitle text. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontSize?: number; + + /** + * Sets the typography style of the subtitle text. Supports values are `b` for + * bold, `i` for italics, and `b_i` for bold with italics. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + typography?: 'b' | 'i' | 'b_i'; +} + +export interface TextOverlay extends BaseOverlay { + /** + * Specifies the text to be displayed in the overlay. The SDK automatically handles + * special characters and encoding. + */ + text: string; + + type: 'text'; + + /** + * Text can be included in the layer as either `i-{input}` (plain text) or + * `ie-{base64_encoded_input}` (base64). By default, the SDK selects the + * appropriate format based on the input text. To always use base64 + * (`ie-{base64}`), set this parameter to `base64`. To always use plain text + * (`i-{input}`), set it to `plain`. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Control styling of the text overlay. See + * [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). + */ + transformation?: Array; +} + +export interface TextOverlayTransformation { + /** + * Specifies the transparency level of the text overlay. Accepts integers from `1` + * to `9`. + */ + alpha?: number; + + /** + * Specifies the background color of the text overlay. Accepts an RGB hex code, an + * RGBA code, or a color name. + */ + background?: string; + + /** + * Flip the text overlay horizontally, vertically, or both. + */ + flip?: 'h' | 'v' | 'h_v' | 'v_h'; + + /** + * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., + * `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + */ + fontColor?: string; + + /** + * Specifies the font family of the overlaid text. Choose from the supported fonts + * list or use a custom font. See + * [Supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + * and + * [Custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). + */ + fontFamily?: string; + + /** + * Specifies the font size of the overlaid text. Accepts a numeric value or an + * arithmetic expression. + */ + fontSize?: number | string; + + /** + * Specifies the inner alignment of the text when width is more than the text + * length. + */ + innerAlignment?: 'left' | 'right' | 'center'; + + /** + * Specifies the line height of the text overlay. Accepts integer values + * representing line height in points. It can also accept + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + * such as `bw_mul_0.2`, or `bh_div_20`. + */ + lineHeight?: number | string; + + /** + * Specifies the padding around the overlaid text. Can be provided as a single + * positive integer or multiple values separated by underscores (following CSS + * shorthand order). Arithmetic expressions are also accepted. + */ + padding?: number | string; + + /** + * Specifies the corner radius of the text overlay. Set to `max` to achieve a + * circular or oval shape. + */ + radius?: number | 'max'; + + /** + * Specifies the rotation angle of the text overlay. Accepts a numeric value for + * clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. + */ + rotation?: number | string; + + /** + * Specifies the typography style of the text. Supported values: + * + * - Single styles: `b` (bold), `i` (italic), `strikethrough`. + * - Combinations: Any combination separated by underscores, e.g., `b_i`, + * `b_i_strikethrough`. + */ + typography?: string; + + /** + * Specifies the maximum width (in pixels) of the overlaid text. The text wraps + * automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are + * supported. Useful when used in conjunction with the `background`. Learn about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + width?: number | string; +} + +/** + * The SDK provides easy-to-use names for transformations. These names are + * converted to the corresponding transformation string before being added to the + * URL. SDKs are updated regularly to support new transformations. If you want to + * use a transformation that is not supported by the SDK, You can use the `raw` + * parameter to pass the transformation string directly. See the + * [Transformations documentation](https://imagekit.io/docs/transformations). + */ +export interface Transformation { + /** + * Uses AI to change the background. Provide a text prompt or a base64-encoded + * prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Not supported inside overlay. See + * [AI Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg). + */ + aiChangeBackground?: string; + + /** + * Adds an AI-based drop shadow around a foreground object on a transparent or + * removed background. Optionally, control the direction, elevation, and saturation + * of the light source (e.g., `az-45` to change light direction). Pass `true` for + * the default drop shadow, or provide a string for a custom drop shadow. Supported + * inside overlay. See + * [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). + */ + aiDropShadow?: true | string; + + /** + * Uses AI to edit images based on a text prompt. Provide a text prompt or a + * base64-encoded prompt, e.g., `prompt-snow road` or + * `prompte-[urlencoded_base64_encoded_text]`. Not supported inside overlay. + * See [AI Edit](https://imagekit.io/docs/ai-transformations#edit-image-e-edit). + */ + aiEdit?: string; + + /** + * Applies ImageKit's in-house background removal. Supported inside overlay. See + * [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). + */ + aiRemoveBackground?: true; + + /** + * Uses third-party background removal. Note: It is recommended to use + * aiRemoveBackground, ImageKit's in-house solution, which is more cost-effective. + * Supported inside overlay. See + * [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). + */ + aiRemoveBackgroundExternal?: true; + + /** + * Performs AI-based retouching to improve faces or product shots. Not supported + * inside overlay. See + * [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). + */ + aiRetouch?: true; + + /** + * Upscales images beyond their original dimensions using AI. Not supported inside + * overlay. See + * [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). + */ + aiUpscale?: true; + + /** + * Generates a variation of an image using AI. This produces a new image with + * slight variations from the original, such as changes in color, texture, and + * other visual elements, while preserving the structure and essence of the + * original image. Not supported inside overlay. See + * [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). + */ + aiVariation?: true; + + /** + * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with + * either width or height (but not both). For example: aspectRatio = `4:3`, `4_3`, + * or an expression like `iar_div_2`. See + * [Image resize and crop – Aspect ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). + */ + aspectRatio?: number | string; + + /** + * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See + * [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). + */ + audioCodec?: 'aac' | 'opus' | 'none'; + + /** + * Specifies the background to be used in conjunction with certain cropping + * strategies when resizing an image. + * + * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. See + * [Solid color background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background). + * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. See + * [Blurred background](https://imagekit.io/docs/effects-and-enhancements#blurred-background). + * - Expand the image boundaries using generative fill: `genfill`. Not supported + * inside overlay. Optionally, control the background scene by passing a text + * prompt: `genfill[:-prompt-${text}]` or + * `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. See + * [Generative fill background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill). + */ + background?: string; + + /** + * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, + * or an expression like `bl-10`. See + * [Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl). + */ + blur?: number; + + /** + * Adds a border to the output media. Accepts a string in the format + * `_` (e.g., `5_FFF000` for a 5px yellow border), or an + * expression like `ih_div_20_FF00FF`. See + * [Border](https://imagekit.io/docs/effects-and-enhancements#border---b). + */ + border?: string; + + /** + * Indicates whether the output image should retain the original color profile. See + * [Color profile](https://imagekit.io/docs/image-optimization#color-profile---cp). + */ + colorProfile?: boolean; + + /** + * Automatically enhances the contrast of an image (contrast stretch). See + * [Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). + */ + contrastStretch?: true; + + /** + * Crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + */ + crop?: 'force' | 'at_max' | 'at_max_enlarge' | 'at_least' | 'maintain_ratio'; + + /** + * Additional crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + */ + cropMode?: 'pad_resize' | 'extract' | 'pad_extract'; + + /** + * Specifies a fallback image if the resource is not found, e.g., a URL or file + * path. See + * [Default image](https://imagekit.io/docs/image-transformation#default-image---di). + */ + defaultImage?: string; + + /** + * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio + * (DPR) calculation. See + * [DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr). + */ + dpr?: number; + + /** + * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to indicate the length from the start offset. + * Arithmetic expressions are supported. See + * [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). + */ + duration?: number | string; + + /** + * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to define a time window. Arithmetic expressions + * are supported. See + * [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). + */ + endOffset?: number | string; + + /** + * Flips or mirrors an image either horizontally, vertically, or both. Acceptable + * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or + * `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). + */ + flip?: 'h' | 'v' | 'h_v' | 'v_h'; + + /** + * Refines padding and cropping behavior for pad resize, maintain ratio, and + * extract crop modes. Supports manual positions and coordinate-based focus. With + * AI-based cropping, you can automatically keep key subjects in frame—such as + * faces or detected objects (e.g., `fo-face`, `fo-person`, `fo-car`)— while + * resizing. + * + * - See [Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo). + * - [Object aware cropping](https://imagekit.io/docs/image-resize-and-crop#object-aware-cropping---fo-object-name) + */ + focus?: string; + + /** + * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, + * `mp4`, or `auto`. You can also pass `orig` for images to return the original + * format. ImageKit automatically delivers images and videos in the optimal format + * based on device support unless overridden by the dashboard settings or the + * format parameter. See + * [Image format](https://imagekit.io/docs/image-optimization#format---f) and + * [Video format](https://imagekit.io/docs/video-optimization#format---f). + */ + format?: 'auto' | 'webp' | 'jpg' | 'jpeg' | 'png' | 'gif' | 'svg' | 'mp4' | 'webm' | 'avif' | 'orig'; + + /** + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. See + * [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + */ + gradient?: true | string; + + /** + * Enables a grayscale effect for images. See + * [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). + */ + grayscale?: true; + + /** + * Specifies the height of the output. If a value between 0 and 1 is provided, it + * is treated as a percentage (e.g., `0.5` represents 50% of the original height). + * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). Height + * transformation – + * [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) + */ + height?: number | string; + + /** + * Specifies whether the output image (in JPEG or PNG) should be compressed + * losslessly. See + * [Lossless compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo). + */ + lossless?: boolean; + + /** + * By default, ImageKit removes all metadata during automatic image compression. + * Set this to true to preserve metadata. See + * [Image metadata](https://imagekit.io/docs/image-optimization#image-metadata---md). + */ + metadata?: boolean; + + /** + * Named transformation reference. See + * [Named transformations](https://imagekit.io/docs/transformations#named-transformations). + */ + named?: string; + + /** + * Specifies the opacity level of the output image. See + * [Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o). + */ + opacity?: number; + + /** + * If set to true, serves the original file without applying any transformations. + * See + * [Deliver original file as-is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true). + */ + original?: boolean; + + /** + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + */ + overlay?: Overlay; + + /** + * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, + * AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the + * 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). See + * [Thumbnail extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). + */ + page?: number | string; + + /** + * Specifies whether the output JPEG image should be rendered progressively. + * Progressive loading begins with a low-quality, pixelated version of the full + * image, which gradually improves to provide a faster perceived load time. See + * [Progressive images](https://imagekit.io/docs/image-optimization#progressive-image---pr). + */ + progressive?: boolean; + + /** + * Specifies the quality of the output image for lossy formats such as JPEG, WebP, + * and AVIF. A higher quality value results in a larger file size with better + * quality, while a lower value produces a smaller file size with reduced quality. + * See [Quality](https://imagekit.io/docs/image-optimization#quality---q). + */ + quality?: number; + + /** + * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular + * or oval shape. See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max'; + + /** + * Pass any transformation not directly supported by the SDK. This transformation + * string is appended to the URL as provided. + */ + raw?: string; + + /** + * Specifies the rotation angle in degrees. Positive values rotate the image + * clockwise; you can also use, for example, `N40` for counterclockwise rotation or + * `auto` to use the orientation specified in the image's EXIF data. For videos, + * only the following values are supported: 0, 90, 180, 270, or 360. See + * [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). + */ + rotation?: number | string; + + /** + * Adds a shadow beneath solid objects in an image with a transparent background. + * For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default + * shadow, or provide a string for a custom shadow. See + * [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). + */ + shadow?: true | string; + + /** + * Sharpens the input image, highlighting edges and finer details. Pass `true` for + * default sharpening, or provide a numeric value for custom sharpening. See + * [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). + */ + sharpen?: true | number; + + /** + * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or + * `10.5`. Arithmetic expressions are also supported. See + * [Trim videos – Start offset](https://imagekit.io/docs/trim-videos#start-offset---so). + */ + startOffset?: number | string; + + /** + * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, + * `480`, `720`, `1080`]. See + * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming). + */ + streamingResolutions?: Array; + + /** + * Useful for images with a solid or nearly solid background and a central object. + * This parameter trims the background, leaving only the central object in the + * output image. See + * [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). + */ + trim?: true | number; + + /** + * Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` for a + * default unsharp mask, or provide a string for a custom unsharp mask. See + * [Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). + */ + unsharpMask?: true | string; + + /** + * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See + * [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). + */ + videoCodec?: 'h264' | 'vp9' | 'av1' | 'none'; + + /** + * Specifies the width of the output. If a value between 0 and 1 is provided, it is + * treated as a percentage (e.g., `0.4` represents 40% of the original width). You + * can also supply arithmetic expressions (e.g., `iw_div_2`). Width transformation + * – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) + */ + width?: number | string; + + /** + * Focus using cropped image coordinates - X coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + x?: number | string; + + /** + * Focus using cropped image coordinates - X center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + xCenter?: number | string; + + /** + * Focus using cropped image coordinates - Y coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + y?: number | string; + + /** + * Focus using cropped image coordinates - Y center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + yCenter?: number | string; + + /** + * Accepts a numeric value that determines how much to zoom in or out of the + * cropped area. It should be used in conjunction with fo-face or fo-. + * See [Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z). + */ + zoom?: number; +} + +/** + * By default, the transformation string is added as a query parameter in the URL, + * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the + * path of the URL, set this to `path`. Learn more in the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ +export type TransformationPosition = 'path' | 'query'; + +export interface VideoOverlay extends BaseOverlay { + /** + * Specifies the relative path to the video used as an overlay. + */ + input: string; + + type: 'video'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Array of transformation to be applied to the overlay video. Except + * `streamingResolutions`, all other video transformations are supported. See + * [Video transformations](https://imagekit.io/docs/video-transformation). + */ + transformation?: Array; +} diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts new file mode 100644 index 00000000..a95d7454 --- /dev/null +++ b/src/resources/webhooks.ts @@ -0,0 +1,999 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import { toBase64 } from '../internal/utils'; +import * as FilesAPI from './files/files'; +import { Webhook } from 'standardwebhooks'; + +export class Webhooks extends APIResource { + unsafeUnwrap(body: string): UnsafeUnwrapWebhookEvent { + return JSON.parse(body) as UnsafeUnwrapWebhookEvent; + } + + unwrap( + body: string, + { headers, key }: { headers: Record; key?: string }, + ): UnwrapWebhookEvent { + if (headers !== undefined) { + const keyStr: string | null = key === undefined ? this._client.webhookSecret : key; + if (keyStr === null) throw new Error('Webhook key must not be null in order to unwrap'); + const wh = new Webhook(toBase64(keyStr)); + wh.verify(body, headers); + } + return JSON.parse(body) as UnwrapWebhookEvent; + } +} + +export interface BaseWebhookEvent { + /** + * Unique identifier for the event. + */ + id: string; + + /** + * The type of webhook event. + */ + type: string; +} + +/** + * Triggered when a post-transformation fails. The original file remains available, + * but the requested transformation could not be generated. + */ +export interface UploadPostTransformErrorEvent extends BaseWebhookEvent { + /** + * Timestamp of when the event occurred in ISO8601 format. + */ + created_at: string; + + data: UploadPostTransformErrorEvent.Data; + + request: UploadPostTransformErrorEvent.Request; + + type: 'upload.post-transform.error'; +} + +export namespace UploadPostTransformErrorEvent { + export interface Data { + /** + * Unique identifier of the originally uploaded file. + */ + fileId: string; + + /** + * Name of the file. + */ + name: string; + + /** + * Path of the file. + */ + path: string; + + transformation: Data.Transformation; + + /** + * URL of the attempted post-transformation. + */ + url: string; + } + + export namespace Data { + export interface Transformation { + error: Transformation.Error; + } + + export namespace Transformation { + export interface Error { + /** + * Reason for the post-transformation failure. + */ + reason: string; + } + } + } + + export interface Request { + transformation: Request.Transformation; + + /** + * Unique identifier for the originating request. + */ + x_request_id: string; + } + + export namespace Request { + export interface Transformation { + /** + * Type of the requested post-transformation. + */ + type: 'transformation' | 'abs' | 'gif-to-video' | 'thumbnail'; + + /** + * Only applicable if transformation type is 'abs'. Streaming protocol used. + */ + protocol?: 'hls' | 'dash'; + + /** + * Value for the requested transformation type. + */ + value?: string; + } + } +} + +/** + * Triggered when a post-transformation completes successfully. The transformed + * version of the file is now ready and can be accessed via the provided URL. Note + * that each post-transformation generates a separate webhook event. + */ +export interface UploadPostTransformSuccessEvent extends BaseWebhookEvent { + /** + * Timestamp of when the event occurred in ISO8601 format. + */ + created_at: string; + + data: UploadPostTransformSuccessEvent.Data; + + request: UploadPostTransformSuccessEvent.Request; + + type: 'upload.post-transform.success'; +} + +export namespace UploadPostTransformSuccessEvent { + export interface Data { + /** + * Unique identifier of the originally uploaded file. + */ + fileId: string; + + /** + * Name of the file. + */ + name: string; + + /** + * URL of the generated post-transformation. + */ + url: string; + } + + export interface Request { + transformation: Request.Transformation; + + /** + * Unique identifier for the originating request. + */ + x_request_id: string; + } + + export namespace Request { + export interface Transformation { + /** + * Type of the requested post-transformation. + */ + type: 'transformation' | 'abs' | 'gif-to-video' | 'thumbnail'; + + /** + * Only applicable if transformation type is 'abs'. Streaming protocol used. + */ + protocol?: 'hls' | 'dash'; + + /** + * Value for the requested transformation type. + */ + value?: string; + } + } +} + +/** + * Triggered when a pre-transformation fails. The file upload may have been + * accepted, but the requested transformation could not be applied. + */ +export interface UploadPreTransformErrorEvent extends BaseWebhookEvent { + /** + * Timestamp of when the event occurred in ISO8601 format. + */ + created_at: string; + + data: UploadPreTransformErrorEvent.Data; + + request: UploadPreTransformErrorEvent.Request; + + type: 'upload.pre-transform.error'; +} + +export namespace UploadPreTransformErrorEvent { + export interface Data { + /** + * Name of the file. + */ + name: string; + + /** + * Path of the file. + */ + path: string; + + transformation: Data.Transformation; + } + + export namespace Data { + export interface Transformation { + error: Transformation.Error; + } + + export namespace Transformation { + export interface Error { + /** + * Reason for the pre-transformation failure. + */ + reason: string; + } + } + } + + export interface Request { + /** + * The requested pre-transformation string. + */ + transformation: string; + + /** + * Unique identifier for the originating request. + */ + x_request_id: string; + } +} + +/** + * Triggered when a pre-transformation completes successfully. The file has been + * processed with the requested transformation and is now available in the Media + * Library. + */ +export interface UploadPreTransformSuccessEvent extends BaseWebhookEvent { + /** + * Timestamp of when the event occurred in ISO8601 format. + */ + created_at: string; + + /** + * Object containing details of a successful upload. + */ + data: UploadPreTransformSuccessEvent.Data; + + request: UploadPreTransformSuccessEvent.Request; + + type: 'upload.pre-transform.success'; +} + +export namespace UploadPreTransformSuccessEvent { + /** + * Object containing details of a successful upload. + */ + export interface Data { + /** + * An array of tags assigned to the uploaded file by auto tagging. + */ + AITags?: Array | null; + + /** + * The audio codec used in the video (only for video). + */ + audioCodec?: string; + + /** + * The bit rate of the video in kbps (only for video). + */ + bitRate?: number; + + /** + * Value of custom coordinates associated with the image in the format + * `x,y,width,height`. If `customCoordinates` are not defined, then it is `null`. + * Send `customCoordinates` in `responseFields` in API request to get the value of + * this field. + */ + customCoordinates?: string | null; + + /** + * A key-value data associated with the asset. Use `responseField` in API request + * to get `customMetadata` in the upload API response. Before setting any custom + * metadata on an asset, you have to create the field using custom metadata fields + * API. Send `customMetadata` in `responseFields` in API request to get the value + * of this field. + */ + customMetadata?: { [key: string]: unknown }; + + /** + * Optional text to describe the contents of the file. Can be set by the user or + * the ai-auto-description extension. + */ + description?: string; + + /** + * The duration of the video in seconds (only for video). + */ + duration?: number; + + /** + * Consolidated embedded metadata associated with the file. It includes exif, iptc, + * and xmp data. Send `embeddedMetadata` in `responseFields` in API request to get + * embeddedMetadata in the upload API response. + */ + embeddedMetadata?: { [key: string]: unknown }; + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + extensionStatus?: Data.ExtensionStatus; + + /** + * Unique fileId. Store this fileld in your database, as this will be used to + * perform update action on this file. + */ + fileId?: string; + + /** + * The relative path of the file in the media library e.g. + * `/marketing-assets/new-banner.jpg`. + */ + filePath?: string; + + /** + * Type of the uploaded file. Possible values are `image`, `non-image`. + */ + fileType?: string; + + /** + * Height of the image in pixels (Only for images) + */ + height?: number; + + /** + * Is the file marked as private. It can be either `true` or `false`. Send + * `isPrivateFile` in `responseFields` in API request to get the value of this + * field. + */ + isPrivateFile?: boolean; + + /** + * Is the file published or in draft state. It can be either `true` or `false`. + * Send `isPublished` in `responseFields` in API request to get the value of this + * field. + */ + isPublished?: boolean; + + /** + * Legacy metadata. Send `metadata` in `responseFields` in API request to get + * metadata in the upload API response. + */ + metadata?: FilesAPI.Metadata; + + /** + * Name of the asset. + */ + name?: string; + + /** + * Size of the image file in Bytes. + */ + size?: number; + + /** + * The array of tags associated with the asset. If no tags are set, it will be + * `null`. Send `tags` in `responseFields` in API request to get the value of this + * field. + */ + tags?: Array | null; + + /** + * In the case of an image, a small thumbnail URL. + */ + thumbnailUrl?: string; + + /** + * A publicly accessible URL of the file. + */ + url?: string; + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + versionInfo?: Data.VersionInfo; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * Width of the image in pixels (Only for Images) + */ + width?: number; + } + + export namespace Data { + export interface AITag { + /** + * Confidence score of the tag. + */ + confidence?: number; + + /** + * Name of the tag. + */ + name?: string; + + /** + * Array of `AITags` associated with the image. If no `AITags` are set, it will be + * null. These tags can be added using the `google-auto-tagging` or + * `aws-auto-tagging` extensions. + */ + source?: string; + } + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + export interface ExtensionStatus { + 'ai-auto-description'?: 'success' | 'pending' | 'failed'; + + 'aws-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'google-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'remove-bg'?: 'success' | 'pending' | 'failed'; + } + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + export interface VersionInfo { + /** + * Unique identifier of the file version. + */ + id?: string; + + /** + * Name of the file version. + */ + name?: string; + } + } + + export interface Request { + /** + * The requested pre-transformation string. + */ + transformation: string; + + /** + * Unique identifier for the originating request. + */ + x_request_id: string; + } +} + +/** + * Triggered when a new video transformation request is accepted for processing. + * This event confirms that ImageKit has received and queued your transformation + * request. Use this for debugging and tracking transformation lifecycle. + */ +export interface VideoTransformationAcceptedEvent extends BaseWebhookEvent { + /** + * Timestamp when the event was created in ISO8601 format. + */ + created_at: string; + + data: VideoTransformationAcceptedEvent.Data; + + /** + * Information about the original request that triggered the video transformation. + */ + request: VideoTransformationAcceptedEvent.Request; + + type: 'video.transformation.accepted'; +} + +export namespace VideoTransformationAcceptedEvent { + export interface Data { + /** + * Information about the source video asset being transformed. + */ + asset: Data.Asset; + + /** + * Base information about a video transformation request. + */ + transformation: Data.Transformation; + } + + export namespace Data { + /** + * Information about the source video asset being transformed. + */ + export interface Asset { + /** + * URL to download or access the source video file. + */ + url: string; + } + + /** + * Base information about a video transformation request. + */ + export interface Transformation { + /** + * Type of video transformation: + * + * - `video-transformation`: Standard video processing (resize, format conversion, + * etc.) + * - `gif-to-video`: Convert animated GIF to video format + * - `video-thumbnail`: Generate thumbnail image from video + */ + type: 'video-transformation' | 'gif-to-video' | 'video-thumbnail'; + + /** + * Configuration options for video transformations. + */ + options?: Transformation.Options; + } + + export namespace Transformation { + /** + * Configuration options for video transformations. + */ + export interface Options { + /** + * Audio codec used for encoding (aac or opus). + */ + audio_codec?: 'aac' | 'opus'; + + /** + * Whether to automatically rotate the video based on metadata. + */ + auto_rotate?: boolean; + + /** + * Output format for the transformed video or thumbnail. + */ + format?: 'mp4' | 'webm' | 'jpg' | 'png' | 'webp'; + + /** + * Quality setting for the output video. + */ + quality?: number; + + /** + * Streaming protocol for adaptive bitrate streaming. + */ + stream_protocol?: 'HLS' | 'DASH'; + + /** + * Array of quality representations for adaptive bitrate streaming. + */ + variants?: Array; + + /** + * Video codec used for encoding (h264, vp9, or av1). + */ + video_codec?: 'h264' | 'vp9' | 'av1'; + } + } + } + + /** + * Information about the original request that triggered the video transformation. + */ + export interface Request { + /** + * Full URL of the transformation request that was submitted. + */ + url: string; + + /** + * Unique identifier for the originating transformation request. + */ + x_request_id: string; + + /** + * User-Agent header from the original request that triggered the transformation. + */ + user_agent?: string; + } +} + +/** + * Triggered when an error occurs during video encoding. Listen to this webhook to + * log error reasons and debug issues. Check your origin and URL endpoint settings + * if the reason is related to download failure. For other errors, contact ImageKit + * support. + */ +export interface VideoTransformationErrorEvent extends BaseWebhookEvent { + /** + * Timestamp when the event was created in ISO8601 format. + */ + created_at: string; + + data: VideoTransformationErrorEvent.Data; + + /** + * Information about the original request that triggered the video transformation. + */ + request: VideoTransformationErrorEvent.Request; + + type: 'video.transformation.error'; +} + +export namespace VideoTransformationErrorEvent { + export interface Data { + /** + * Information about the source video asset being transformed. + */ + asset: Data.Asset; + + transformation: Data.Transformation; + } + + export namespace Data { + /** + * Information about the source video asset being transformed. + */ + export interface Asset { + /** + * URL to download or access the source video file. + */ + url: string; + } + + export interface Transformation { + /** + * Type of video transformation: + * + * - `video-transformation`: Standard video processing (resize, format conversion, + * etc.) + * - `gif-to-video`: Convert animated GIF to video format + * - `video-thumbnail`: Generate thumbnail image from video + */ + type: 'video-transformation' | 'gif-to-video' | 'video-thumbnail'; + + /** + * Details about the transformation error. + */ + error?: Transformation.Error; + + /** + * Configuration options for video transformations. + */ + options?: Transformation.Options; + } + + export namespace Transformation { + /** + * Details about the transformation error. + */ + export interface Error { + /** + * Specific reason for the transformation failure: + * + * - `encoding_failed`: Error during video encoding process + * - `download_failed`: Could not download source video + * - `internal_server_error`: Unexpected server error + */ + reason: 'encoding_failed' | 'download_failed' | 'internal_server_error'; + } + + /** + * Configuration options for video transformations. + */ + export interface Options { + /** + * Audio codec used for encoding (aac or opus). + */ + audio_codec?: 'aac' | 'opus'; + + /** + * Whether to automatically rotate the video based on metadata. + */ + auto_rotate?: boolean; + + /** + * Output format for the transformed video or thumbnail. + */ + format?: 'mp4' | 'webm' | 'jpg' | 'png' | 'webp'; + + /** + * Quality setting for the output video. + */ + quality?: number; + + /** + * Streaming protocol for adaptive bitrate streaming. + */ + stream_protocol?: 'HLS' | 'DASH'; + + /** + * Array of quality representations for adaptive bitrate streaming. + */ + variants?: Array; + + /** + * Video codec used for encoding (h264, vp9, or av1). + */ + video_codec?: 'h264' | 'vp9' | 'av1'; + } + } + } + + /** + * Information about the original request that triggered the video transformation. + */ + export interface Request { + /** + * Full URL of the transformation request that was submitted. + */ + url: string; + + /** + * Unique identifier for the originating transformation request. + */ + x_request_id: string; + + /** + * User-Agent header from the original request that triggered the transformation. + */ + user_agent?: string; + } +} + +/** + * Triggered when video encoding is finished and the transformed resource is ready + * to be served. This is the key event to listen for - update your database or CMS + * flags when you receive this so your application can start showing the + * transformed video to users. + */ +export interface VideoTransformationReadyEvent extends BaseWebhookEvent { + /** + * Timestamp when the event was created in ISO8601 format. + */ + created_at: string; + + data: VideoTransformationReadyEvent.Data; + + /** + * Information about the original request that triggered the video transformation. + */ + request: VideoTransformationReadyEvent.Request; + + type: 'video.transformation.ready'; + + /** + * Performance metrics for the transformation process. + */ + timings?: VideoTransformationReadyEvent.Timings; +} + +export namespace VideoTransformationReadyEvent { + export interface Data { + /** + * Information about the source video asset being transformed. + */ + asset: Data.Asset; + + transformation: Data.Transformation; + } + + export namespace Data { + /** + * Information about the source video asset being transformed. + */ + export interface Asset { + /** + * URL to download or access the source video file. + */ + url: string; + } + + export interface Transformation { + /** + * Type of video transformation: + * + * - `video-transformation`: Standard video processing (resize, format conversion, + * etc.) + * - `gif-to-video`: Convert animated GIF to video format + * - `video-thumbnail`: Generate thumbnail image from video + */ + type: 'video-transformation' | 'gif-to-video' | 'video-thumbnail'; + + /** + * Configuration options for video transformations. + */ + options?: Transformation.Options; + + /** + * Information about the transformed output video. + */ + output?: Transformation.Output; + } + + export namespace Transformation { + /** + * Configuration options for video transformations. + */ + export interface Options { + /** + * Audio codec used for encoding (aac or opus). + */ + audio_codec?: 'aac' | 'opus'; + + /** + * Whether to automatically rotate the video based on metadata. + */ + auto_rotate?: boolean; + + /** + * Output format for the transformed video or thumbnail. + */ + format?: 'mp4' | 'webm' | 'jpg' | 'png' | 'webp'; + + /** + * Quality setting for the output video. + */ + quality?: number; + + /** + * Streaming protocol for adaptive bitrate streaming. + */ + stream_protocol?: 'HLS' | 'DASH'; + + /** + * Array of quality representations for adaptive bitrate streaming. + */ + variants?: Array; + + /** + * Video codec used for encoding (h264, vp9, or av1). + */ + video_codec?: 'h264' | 'vp9' | 'av1'; + } + + /** + * Information about the transformed output video. + */ + export interface Output { + /** + * URL to access the transformed video. + */ + url: string; + + /** + * Metadata of the output video file. + */ + video_metadata?: Output.VideoMetadata; + } + + export namespace Output { + /** + * Metadata of the output video file. + */ + export interface VideoMetadata { + /** + * Bitrate of the output video in bits per second. + */ + bitrate: number; + + /** + * Duration of the output video in seconds. + */ + duration: number; + + /** + * Height of the output video in pixels. + */ + height: number; + + /** + * Width of the output video in pixels. + */ + width: number; + } + } + } + } + + /** + * Information about the original request that triggered the video transformation. + */ + export interface Request { + /** + * Full URL of the transformation request that was submitted. + */ + url: string; + + /** + * Unique identifier for the originating transformation request. + */ + x_request_id: string; + + /** + * User-Agent header from the original request that triggered the transformation. + */ + user_agent?: string; + } + + /** + * Performance metrics for the transformation process. + */ + export interface Timings { + /** + * Time spent downloading the source video from your origin or media library, in + * milliseconds. + */ + download_duration?: number; + + /** + * Time spent encoding the video, in milliseconds. + */ + encoding_duration?: number; + } +} + +/** + * Triggered when a new video transformation request is accepted for processing. + * This event confirms that ImageKit has received and queued your transformation + * request. Use this for debugging and tracking transformation lifecycle. + */ +export type UnsafeUnwrapWebhookEvent = + | VideoTransformationAcceptedEvent + | VideoTransformationReadyEvent + | VideoTransformationErrorEvent + | UploadPreTransformSuccessEvent + | UploadPreTransformErrorEvent + | UploadPostTransformSuccessEvent + | UploadPostTransformErrorEvent; + +/** + * Triggered when a new video transformation request is accepted for processing. + * This event confirms that ImageKit has received and queued your transformation + * request. Use this for debugging and tracking transformation lifecycle. + */ +export type UnwrapWebhookEvent = + | VideoTransformationAcceptedEvent + | VideoTransformationReadyEvent + | VideoTransformationErrorEvent + | UploadPreTransformSuccessEvent + | UploadPreTransformErrorEvent + | UploadPostTransformSuccessEvent + | UploadPostTransformErrorEvent; + +export declare namespace Webhooks { + export { + type BaseWebhookEvent as BaseWebhookEvent, + type UploadPostTransformErrorEvent as UploadPostTransformErrorEvent, + type UploadPostTransformSuccessEvent as UploadPostTransformSuccessEvent, + type UploadPreTransformErrorEvent as UploadPreTransformErrorEvent, + type UploadPreTransformSuccessEvent as UploadPreTransformSuccessEvent, + type VideoTransformationAcceptedEvent as VideoTransformationAcceptedEvent, + type VideoTransformationErrorEvent as VideoTransformationErrorEvent, + type VideoTransformationReadyEvent as VideoTransformationReadyEvent, + type UnsafeUnwrapWebhookEvent as UnsafeUnwrapWebhookEvent, + type UnwrapWebhookEvent as UnwrapWebhookEvent, + }; +} diff --git a/src/uploads.ts b/src/uploads.ts new file mode 100644 index 00000000..b2ef6471 --- /dev/null +++ b/src/uploads.ts @@ -0,0 +1,2 @@ +/** @deprecated Import from ./core/uploads instead */ +export * from './core/uploads'; diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 00000000..e1f023d3 --- /dev/null +++ b/src/version.ts @@ -0,0 +1 @@ +export const VERSION = '7.0.0'; // x-release-please-version diff --git a/test-e2e.sh b/test-e2e.sh deleted file mode 100644 index e9aa713f..00000000 --- a/test-e2e.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -npm pack -cd tests/e2e -export YARN_CACHE_FOLDER=.cache -cd node-js -rm -rf .cache -yarn init --yes -echo "Installing local bundle from TAR in NodeJS project" -yarn add ../../../imagekit*.tgz -node index.js;test_result=$? -echo $test_result -if [ "$test_result" != "0" ]; then - printf '%s\n' "Final bundle not working in NodeJS project" >&2 - exit 1 -fi -echo "Final bundle working in NodeJS project" - -cd ../typescript -rm -rf .cache -yarn init --yes -yarn add typescript --dev -echo "Installing local bundle from TAR in Typescript project" -yarn add ../../../imagekit*.tgz -npx tsc && node index.js;test_result=$? -echo $test_result -if [ "$test_result" != "0" ]; then - printf '%s\n' "Final bundle not working in Typescript project" >&2 - exit 1 -fi -echo "Final bundle working in Typescript project" - -rm -rf ../../../imagekit*.tgz \ No newline at end of file diff --git a/tests/api-resources/accounts/origins.test.ts b/tests/api-resources/accounts/origins.test.ts new file mode 100644 index 00000000..f912c7ea --- /dev/null +++ b/tests/api-resources/accounts/origins.test.ts @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource origins', () => { + // Prism tests are disabled + test.skip('create: only required params', async () => { + const responsePromise = client.accounts.origins.create({ + accessKey: 'AKIAIOSFODNN7EXAMPLE', + bucket: 'product-images', + name: 'US S3 Storage', + secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + type: 'S3', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('create: required and optional params', async () => { + const response = await client.accounts.origins.create({ + accessKey: 'AKIAIOSFODNN7EXAMPLE', + bucket: 'product-images', + name: 'US S3 Storage', + secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + type: 'S3', + baseUrlForCanonicalHeader: 'https://cdn.example.com', + includeCanonicalHeader: false, + prefix: 'raw-assets', + }); + }); + + // Prism tests are disabled + test.skip('update: only required params', async () => { + const responsePromise = client.accounts.origins.update('id', { + accessKey: 'AKIAIOSFODNN7EXAMPLE', + bucket: 'product-images', + name: 'US S3 Storage', + secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + type: 'S3', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('update: required and optional params', async () => { + const response = await client.accounts.origins.update('id', { + accessKey: 'AKIAIOSFODNN7EXAMPLE', + bucket: 'product-images', + name: 'US S3 Storage', + secretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + type: 'S3', + baseUrlForCanonicalHeader: 'https://cdn.example.com', + includeCanonicalHeader: false, + prefix: 'raw-assets', + }); + }); + + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.accounts.origins.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.accounts.origins.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.accounts.origins.get('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/accounts/url-endpoints.test.ts b/tests/api-resources/accounts/url-endpoints.test.ts new file mode 100644 index 00000000..eea18604 --- /dev/null +++ b/tests/api-resources/accounts/url-endpoints.test.ts @@ -0,0 +1,93 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource urlEndpoints', () => { + // Prism tests are disabled + test.skip('create: only required params', async () => { + const responsePromise = client.accounts.urlEndpoints.create({ description: 'My custom URL endpoint' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('create: required and optional params', async () => { + const response = await client.accounts.urlEndpoints.create({ + description: 'My custom URL endpoint', + origins: ['origin-id-1'], + urlPrefix: 'product-images', + urlRewriter: { type: 'CLOUDINARY', preserveAssetDeliveryTypes: true }, + }); + }); + + // Prism tests are disabled + test.skip('update: only required params', async () => { + const responsePromise = client.accounts.urlEndpoints.update('id', { + description: 'My custom URL endpoint', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('update: required and optional params', async () => { + const response = await client.accounts.urlEndpoints.update('id', { + description: 'My custom URL endpoint', + origins: ['origin-id-1'], + urlPrefix: 'product-images', + urlRewriter: { type: 'CLOUDINARY', preserveAssetDeliveryTypes: true }, + }); + }); + + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.accounts.urlEndpoints.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.accounts.urlEndpoints.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.accounts.urlEndpoints.get('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/accounts/usage.test.ts b/tests/api-resources/accounts/usage.test.ts new file mode 100644 index 00000000..98f51e3f --- /dev/null +++ b/tests/api-resources/accounts/usage.test.ts @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource usage', () => { + // Prism tests are disabled + test.skip('get: only required params', async () => { + const responsePromise = client.accounts.usage.get({ endDate: '2019-12-27', startDate: '2019-12-27' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('get: required and optional params', async () => { + const response = await client.accounts.usage.get({ endDate: '2019-12-27', startDate: '2019-12-27' }); + }); +}); diff --git a/tests/api-resources/assets.test.ts b/tests/api-resources/assets.test.ts new file mode 100644 index 00000000..4688d817 --- /dev/null +++ b/tests/api-resources/assets.test.ts @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource assets', () => { + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.assets.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('list: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.assets.list( + { + fileType: 'all', + limit: 1, + path: 'path', + searchQuery: 'searchQuery', + skip: 0, + sort: 'ASC_NAME', + type: 'file', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(ImageKit.NotFoundError); + }); +}); diff --git a/tests/api-resources/beta/v2/files.test.ts b/tests/api-resources/beta/v2/files.test.ts new file mode 100644 index 00000000..0faf8d31 --- /dev/null +++ b/tests/api-resources/beta/v2/files.test.ts @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit, { toFile } from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource files', () => { + // Prism tests are disabled + test.skip('upload: only required params', async () => { + const responsePromise = client.beta.v2.files.upload({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + fileName: 'fileName', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('upload: required and optional params', async () => { + const response = await client.beta.v2.files.upload({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + fileName: 'fileName', + token: 'token', + checks: '"request.folder" : "marketing/"\n', + customCoordinates: 'customCoordinates', + customMetadata: { brand: 'bar', color: 'bar' }, + description: 'Running shoes', + extensions: [ + { + name: 'remove-bg', + options: { + add_shadow: true, + bg_color: 'bg_color', + bg_image_url: 'bg_image_url', + semitransparency: true, + }, + }, + { maxTags: 5, minConfidence: 95, name: 'google-auto-tagging' }, + { name: 'ai-auto-description' }, + ], + folder: 'folder', + isPrivateFile: true, + isPublished: true, + overwriteAITags: true, + overwriteCustomMetadata: true, + overwriteFile: true, + overwriteTags: true, + responseFields: ['tags', 'customCoordinates', 'isPrivateFile'], + tags: ['t-shirt', 'round-neck', 'men'], + transformation: { + post: [ + { type: 'thumbnail', value: 'w-150,h-150' }, + { protocol: 'dash', type: 'abs', value: 'sr-240_360_480_720_1080' }, + ], + pre: 'w-300,h-300,q-80', + }, + useUniqueFileName: true, + webhookUrl: 'https://example.com', + }); + }); +}); diff --git a/tests/api-resources/cache/invalidation.test.ts b/tests/api-resources/cache/invalidation.test.ts new file mode 100644 index 00000000..02f3d4cc --- /dev/null +++ b/tests/api-resources/cache/invalidation.test.ts @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource invalidation', () => { + // Prism tests are disabled + test.skip('create: only required params', async () => { + const responsePromise = client.cache.invalidation.create({ + url: 'https://ik.imagekit.io/your_imagekit_id/default-image.jpg', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('create: required and optional params', async () => { + const response = await client.cache.invalidation.create({ + url: 'https://ik.imagekit.io/your_imagekit_id/default-image.jpg', + }); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.cache.invalidation.get('requestId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/custom-metadata-fields.test.ts b/tests/api-resources/custom-metadata-fields.test.ts new file mode 100644 index 00000000..cbc88761 --- /dev/null +++ b/tests/api-resources/custom-metadata-fields.test.ts @@ -0,0 +1,112 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource customMetadataFields', () => { + // Prism tests are disabled + test.skip('create: only required params', async () => { + const responsePromise = client.customMetadataFields.create({ + label: 'price', + name: 'price', + schema: { type: 'Number' }, + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('create: required and optional params', async () => { + const response = await client.customMetadataFields.create({ + label: 'price', + name: 'price', + schema: { + type: 'Number', + defaultValue: 'string', + isValueRequired: true, + maxLength: 0, + maxValue: 3000, + minLength: 0, + minValue: 1000, + selectOptions: ['small', 'medium', 'large', 30, 40, true], + }, + }); + }); + + // Prism tests are disabled + test.skip('update', async () => { + const responsePromise = client.customMetadataFields.update('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.customMetadataFields.update( + 'id', + { + label: 'price', + schema: { + defaultValue: 'string', + isValueRequired: true, + maxLength: 0, + maxValue: 3000, + minLength: 0, + minValue: 1000, + selectOptions: ['small', 'medium', 'large', 30, 40, true], + }, + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(ImageKit.NotFoundError); + }); + + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.customMetadataFields.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('list: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.customMetadataFields.list({ includeDeleted: true }, { path: '/_stainless_unknown_path' }), + ).rejects.toThrow(ImageKit.NotFoundError); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.customMetadataFields.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/files/bulk.test.ts b/tests/api-resources/files/bulk.test.ts new file mode 100644 index 00000000..c5c290a7 --- /dev/null +++ b/tests/api-resources/files/bulk.test.ts @@ -0,0 +1,101 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource bulk', () => { + // Prism tests are disabled + test.skip('delete: only required params', async () => { + const responsePromise = client.files.bulk.delete({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete: required and optional params', async () => { + const response = await client.files.bulk.delete({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + }); + }); + + // Prism tests are disabled + test.skip('addTags: only required params', async () => { + const responsePromise = client.files.bulk.addTags({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + tags: ['t-shirt', 'round-neck', 'sale2019'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('addTags: required and optional params', async () => { + const response = await client.files.bulk.addTags({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + tags: ['t-shirt', 'round-neck', 'sale2019'], + }); + }); + + // Prism tests are disabled + test.skip('removeAITags: only required params', async () => { + const responsePromise = client.files.bulk.removeAITags({ + AITags: ['t-shirt', 'round-neck', 'sale2019'], + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('removeAITags: required and optional params', async () => { + const response = await client.files.bulk.removeAITags({ + AITags: ['t-shirt', 'round-neck', 'sale2019'], + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + }); + }); + + // Prism tests are disabled + test.skip('removeTags: only required params', async () => { + const responsePromise = client.files.bulk.removeTags({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + tags: ['t-shirt', 'round-neck', 'sale2019'], + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('removeTags: required and optional params', async () => { + const response = await client.files.bulk.removeTags({ + fileIds: ['598821f949c0a938d57563bd', '598821f949c0a938d57563be'], + tags: ['t-shirt', 'round-neck', 'sale2019'], + }); + }); +}); diff --git a/tests/api-resources/files/files.test.ts b/tests/api-resources/files/files.test.ts new file mode 100644 index 00000000..3187856d --- /dev/null +++ b/tests/api-resources/files/files.test.ts @@ -0,0 +1,180 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit, { toFile } from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource files', () => { + // Prism tests are disabled + test.skip('update', async () => { + const responsePromise = client.files.update('fileId', {}); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.files.delete('fileId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('copy: only required params', async () => { + const responsePromise = client.files.copy({ + destinationPath: '/folder/to/copy/into/', + sourceFilePath: '/path/to/file.jpg', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('copy: required and optional params', async () => { + const response = await client.files.copy({ + destinationPath: '/folder/to/copy/into/', + sourceFilePath: '/path/to/file.jpg', + includeFileVersions: false, + }); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.files.get('fileId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('move: only required params', async () => { + const responsePromise = client.files.move({ + destinationPath: '/folder/to/move/into/', + sourceFilePath: '/path/to/file.jpg', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('move: required and optional params', async () => { + const response = await client.files.move({ + destinationPath: '/folder/to/move/into/', + sourceFilePath: '/path/to/file.jpg', + }); + }); + + // Prism tests are disabled + test.skip('rename: only required params', async () => { + const responsePromise = client.files.rename({ + filePath: '/path/to/file.jpg', + newFileName: 'newFileName.jpg', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('rename: required and optional params', async () => { + const response = await client.files.rename({ + filePath: '/path/to/file.jpg', + newFileName: 'newFileName.jpg', + purgeCache: true, + }); + }); + + // Prism tests are disabled + test.skip('upload: only required params', async () => { + const responsePromise = client.files.upload({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + fileName: 'fileName', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('upload: required and optional params', async () => { + const response = await client.files.upload({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + fileName: 'fileName', + token: 'token', + checks: '"request.folder" : "marketing/"\n', + customCoordinates: 'customCoordinates', + customMetadata: { brand: 'bar', color: 'bar' }, + description: 'Running shoes', + expire: 0, + extensions: [ + { + name: 'remove-bg', + options: { + add_shadow: true, + bg_color: 'bg_color', + bg_image_url: 'bg_image_url', + semitransparency: true, + }, + }, + { maxTags: 5, minConfidence: 95, name: 'google-auto-tagging' }, + { name: 'ai-auto-description' }, + ], + folder: 'folder', + isPrivateFile: true, + isPublished: true, + overwriteAITags: true, + overwriteCustomMetadata: true, + overwriteFile: true, + overwriteTags: true, + publicKey: 'publicKey', + responseFields: ['tags', 'customCoordinates', 'isPrivateFile'], + signature: 'signature', + tags: ['t-shirt', 'round-neck', 'men'], + transformation: { + post: [ + { type: 'thumbnail', value: 'w-150,h-150' }, + { protocol: 'dash', type: 'abs', value: 'sr-240_360_480_720_1080' }, + ], + pre: 'w-300,h-300,q-80', + }, + useUniqueFileName: true, + webhookUrl: 'https://example.com', + }); + }); +}); diff --git a/tests/api-resources/files/metadata.test.ts b/tests/api-resources/files/metadata.test.ts new file mode 100644 index 00000000..b7da8ebf --- /dev/null +++ b/tests/api-resources/files/metadata.test.ts @@ -0,0 +1,40 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource metadata', () => { + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.files.metadata.get('fileId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('getFromURL: only required params', async () => { + const responsePromise = client.files.metadata.getFromURL({ url: 'https://example.com' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('getFromURL: required and optional params', async () => { + const response = await client.files.metadata.getFromURL({ url: 'https://example.com' }); + }); +}); diff --git a/tests/api-resources/files/versions.test.ts b/tests/api-resources/files/versions.test.ts new file mode 100644 index 00000000..c1bb6d4c --- /dev/null +++ b/tests/api-resources/files/versions.test.ts @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource versions', () => { + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.files.versions.list('fileId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete: only required params', async () => { + const responsePromise = client.files.versions.delete('versionId', { fileId: 'fileId' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete: required and optional params', async () => { + const response = await client.files.versions.delete('versionId', { fileId: 'fileId' }); + }); + + // Prism tests are disabled + test.skip('get: only required params', async () => { + const responsePromise = client.files.versions.get('versionId', { fileId: 'fileId' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('get: required and optional params', async () => { + const response = await client.files.versions.get('versionId', { fileId: 'fileId' }); + }); + + // Prism tests are disabled + test.skip('restore: only required params', async () => { + const responsePromise = client.files.versions.restore('versionId', { fileId: 'fileId' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('restore: required and optional params', async () => { + const response = await client.files.versions.restore('versionId', { fileId: 'fileId' }); + }); +}); diff --git a/tests/api-resources/folders/folders.test.ts b/tests/api-resources/folders/folders.test.ts new file mode 100644 index 00000000..a983ec4a --- /dev/null +++ b/tests/api-resources/folders/folders.test.ts @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource folders', () => { + // Prism tests are disabled + test.skip('create: only required params', async () => { + const responsePromise = client.folders.create({ + folderName: 'summer', + parentFolderPath: '/product/images/', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('create: required and optional params', async () => { + const response = await client.folders.create({ + folderName: 'summer', + parentFolderPath: '/product/images/', + }); + }); + + // Prism tests are disabled + test.skip('delete: only required params', async () => { + const responsePromise = client.folders.delete({ folderPath: '/folder/to/delete/' }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('delete: required and optional params', async () => { + const response = await client.folders.delete({ folderPath: '/folder/to/delete/' }); + }); + + // Prism tests are disabled + test.skip('copy: only required params', async () => { + const responsePromise = client.folders.copy({ + destinationPath: '/path/of/destination/folder', + sourceFolderPath: '/path/of/source/folder', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('copy: required and optional params', async () => { + const response = await client.folders.copy({ + destinationPath: '/path/of/destination/folder', + sourceFolderPath: '/path/of/source/folder', + includeVersions: true, + }); + }); + + // Prism tests are disabled + test.skip('move: only required params', async () => { + const responsePromise = client.folders.move({ + destinationPath: '/path/of/destination/folder', + sourceFolderPath: '/path/of/source/folder', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('move: required and optional params', async () => { + const response = await client.folders.move({ + destinationPath: '/path/of/destination/folder', + sourceFolderPath: '/path/of/source/folder', + }); + }); + + // Prism tests are disabled + test.skip('rename: only required params', async () => { + const responsePromise = client.folders.rename({ + folderPath: '/path/of/folder', + newFolderName: 'new-folder-name', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('rename: required and optional params', async () => { + const response = await client.folders.rename({ + folderPath: '/path/of/folder', + newFolderName: 'new-folder-name', + purgeCache: true, + }); + }); +}); diff --git a/tests/api-resources/folders/job.test.ts b/tests/api-resources/folders/job.test.ts new file mode 100644 index 00000000..e15071f7 --- /dev/null +++ b/tests/api-resources/folders/job.test.ts @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource job', () => { + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.folders.job.get('jobId'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); +}); diff --git a/tests/api-resources/webhooks.test.ts b/tests/api-resources/webhooks.test.ts new file mode 100644 index 00000000..8fd83de9 --- /dev/null +++ b/tests/api-resources/webhooks.test.ts @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { Webhook } from 'standardwebhooks'; + +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource webhooks', () => { + test.skip('unwrap', async () => { + const key = 'whsec_c2VjcmV0Cg=='; + const payload = + '{"id":"id","type":"video.transformation.accepted","created_at":"2019-12-27T18:11:19.117Z","data":{"asset":{"url":"https://example.com"},"transformation":{"type":"video-transformation","options":{"audio_codec":"aac","auto_rotate":true,"format":"mp4","quality":0,"stream_protocol":"HLS","variants":["string"],"video_codec":"h264"}}},"request":{"url":"https://example.com","x_request_id":"x_request_id","user_agent":"user_agent"}}'; + const msgID = '1'; + const timestamp = new Date(); + const wh = new Webhook(key); + const signature = wh.sign(msgID, timestamp, payload); + const headers: Record = { + 'webhook-signature': signature, + 'webhook-id': msgID, + 'webhook-timestamp': String(Math.floor(timestamp.getTime() / 1000)), + }; + client.webhooks.unwrap(payload, { headers, key }); + expect(() => { + const wrongKey = 'whsec_aaaaaaaaaa=='; + client.webhooks.unwrap(payload, { headers, key: wrongKey }); + }).toThrow('No matching signature found'); + expect(() => { + const badSig = wh.sign(msgID, timestamp, 'some other payload'); + client.webhooks.unwrap(payload, { headers: { ...headers, 'webhook-signature': badSig }, key }); + }).toThrow('No matching signature found'); + expect(() => { + client.webhooks.unwrap(payload, { headers: { ...headers, 'webhook-timestamp': '5' }, key }); + }).toThrow('Message timestamp too old'); + expect(() => { + client.webhooks.unwrap(payload, { headers: { ...headers, 'webhook-id': 'wrong' }, key }); + }).toThrow('No matching signature found'); + }); +}); diff --git a/tests/base64.test.ts b/tests/base64.test.ts new file mode 100644 index 00000000..51910051 --- /dev/null +++ b/tests/base64.test.ts @@ -0,0 +1,80 @@ +import { fromBase64, toBase64 } from '@imagekit/nodejs/internal/utils/base64'; + +describe.each(['Buffer', 'atob'])('with %s', (mode) => { + let originalBuffer: BufferConstructor; + beforeAll(() => { + if (mode === 'atob') { + originalBuffer = globalThis.Buffer; + // @ts-expect-error Can't assign undefined to BufferConstructor + delete globalThis.Buffer; + } + }); + afterAll(() => { + if (mode === 'atob') { + globalThis.Buffer = originalBuffer; + } + }); + test('toBase64', () => { + const testCases = [ + { + input: 'hello world', + expected: 'aGVsbG8gd29ybGQ=', + }, + { + input: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]), + expected: 'aGVsbG8gd29ybGQ=', + }, + { + input: undefined, + expected: '', + }, + { + input: new Uint8Array([ + 229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123, + 193, 71, + ]), + expected: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH', + }, + { + input: '✓', + expected: '4pyT', + }, + { + input: new Uint8Array([226, 156, 147]), + expected: '4pyT', + }, + ]; + + testCases.forEach(({ input, expected }) => { + expect(toBase64(input)).toBe(expected); + }); + }); + + test('fromBase64', () => { + const testCases = [ + { + input: 'aGVsbG8gd29ybGQ=', + expected: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]), + }, + { + input: '', + expected: new Uint8Array([]), + }, + { + input: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH', + expected: new Uint8Array([ + 229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123, + 193, 71, + ]), + }, + { + input: '4pyT', + expected: new Uint8Array([226, 156, 147]), + }, + ]; + + testCases.forEach(({ input, expected }) => { + expect(fromBase64(input)).toEqual(expected); + }); + }); +}); diff --git a/tests/buildHeaders.test.ts b/tests/buildHeaders.test.ts new file mode 100644 index 00000000..818d0b89 --- /dev/null +++ b/tests/buildHeaders.test.ts @@ -0,0 +1,88 @@ +import { inspect } from 'node:util'; +import { buildHeaders, type HeadersLike, type NullableHeaders } from '@imagekit/nodejs/internal/headers'; + +function inspectNullableHeaders(headers: NullableHeaders) { + return `NullableHeaders {${[ + ...[...headers.values.entries()].map(([name, value]) => ` ${inspect(name)}: ${inspect(value)}`), + ...[...headers.nulls].map((name) => ` ${inspect(name)}: null`), + ].join(', ')} }`; +} + +describe('buildHeaders', () => { + const cases: [HeadersLike[], string][] = [ + [[new Headers({ 'content-type': 'text/plain' })], `NullableHeaders { 'content-type': 'text/plain' }`], + [ + [ + { + 'content-type': 'text/plain', + }, + { + 'Content-Type': undefined, + }, + ], + `NullableHeaders { 'content-type': 'text/plain' }`, + ], + [ + [ + { + 'content-type': 'text/plain', + }, + { + 'Content-Type': null, + }, + ], + `NullableHeaders { 'content-type': null }`, + ], + [ + [ + { + cookie: 'name1=value1', + Cookie: 'name2=value2', + }, + ], + `NullableHeaders { 'cookie': 'name2=value2' }`, + ], + [ + [ + { + cookie: 'name1=value1', + Cookie: undefined, + }, + ], + `NullableHeaders { 'cookie': 'name1=value1' }`, + ], + [ + [ + { + cookie: ['name1=value1', 'name2=value2'], + }, + ], + `NullableHeaders { 'cookie': 'name1=value1; name2=value2' }`, + ], + [ + [ + { + 'x-foo': ['name1=value1', 'name2=value2'], + }, + ], + `NullableHeaders { 'x-foo': 'name1=value1, name2=value2' }`, + ], + [ + [ + [ + ['cookie', 'name1=value1'], + ['cookie', 'name2=value2'], + ['Cookie', 'name3=value3'], + ], + ], + `NullableHeaders { 'cookie': 'name1=value1; name2=value2; name3=value3' }`, + ], + [[undefined], `NullableHeaders { }`], + [[null], `NullableHeaders { }`], + ]; + for (const [input, expected] of cases) { + test(expected, () => { + expect(inspectNullableHeaders(buildHeaders(input))).toEqual(expected); + }); + } +}); diff --git a/tests/cache.js b/tests/cache.js deleted file mode 100644 index f1f7b2c4..00000000 --- a/tests/cache.js +++ /dev/null @@ -1,186 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Cache purge API", function () { - describe("Request body check", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - url: url - }); - done(); - return [200]; - }) - - imagekit.purgeCache(url); - }); - - it('Purge cache no url', function (done) { - imagekit.purgeCache("", function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing URL parameter for this request" - }); - done(); - }); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - done(); - return [200]; - }) - - imagekit.getPurgeCacheStatus(requestId); - }); - - it('Purge cache missing requestId', function (done) { - imagekit.getPurgeCacheStatus("", function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing Request ID parameter for this request" - }); - done(); - }); - }); - }); - - describe("Success callbacks", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.purgeCache(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getPurgeCacheStatus(requestId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Purge cache promise', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - var response = await imagekit.getPurgeCacheStatus(requestId); - expect(response).to.be.deep.equal(dummyAPISuccessResponse); - return Promise.resolve(); - }); - }); - - describe("Error callbacks", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.purgeCache(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getPurgeCacheStatus(requestId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Purge cache promise', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch(ex) { - expect(ex).to.be.deep.equal(dummyAPIErrorResponse); - return Promise.resolve(); - } - - return Promise.reject(); - }); - }); -}); - diff --git a/tests/custom-metadata-field.js b/tests/custom-metadata-field.js deleted file mode 100644 index 18ad8cac..00000000 --- a/tests/custom-metadata-field.js +++ /dev/null @@ -1,392 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - id: "id", - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Custom metadata field API", function () { - describe("Request body check", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }); - done(); - return [200]; - }) - - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }); - }); - - it('Create field missing name', function (done) { - imagekit.createCustomMetadataField({ - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing name parameter for this request" - }); - done(); - }); - }); - - it('Create field missing label', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - schema: { - type: "number", - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing label parameter for this request" - }); - done(); - }); - }); - - it('Create field missing schema', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - label: "label" - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing schema parameter for this request" - }); - done(); - }); - }); - - it('Create field missing schema.type', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "schema should have a mandatory type field.", - message: "Invalid value for schema" - }); - done(); - }); - }); - - it('Get field', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [200]; - }) - - imagekit.getCustomMetadataFields(); - }); - - it('Get field - includeDeleted true', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: true - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [200]; - }) - - imagekit.getCustomMetadataFields({ - includeDeleted: true - }); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - schema: { - minValue: 10 - } - }); - done(); - return [200]; - }) - - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 10 - } - }); - }); - - it('Update field only label', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - label: "new-label" - }); - done(); - return [200]; - }) - - imagekit.updateCustomMetadataField("fieldId", { - label: "new-label" - }); - }); - - it('Update field missing fieldId', function (done) { - imagekit.updateCustomMetadataField(null, {}, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing fieldId parameter for this request" - }); - done(); - }); - }); - - it('Update field missing label and schema', function (done) { - imagekit.updateCustomMetadataField("fieldId", {}, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Both label and schema is missing" - }); - done(); - }); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [204]; - }) - - imagekit.deleteCustomMetadataField("fieldId"); - }); - - it('Delete field missing fieldId', function (done) { - imagekit.deleteCustomMetadataField(null, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing fieldId parameter for this request" - }); - done(); - }); - }); - - }); - - describe("Success callbacks", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get fields', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, [dummyAPISuccessResponse, dummyAPISuccessResponse]) - - var callback = sinon.spy(); - imagekit.getCustomMetadataFields({}, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, [dummyAPISuccessResponse, dummyAPISuccessResponse]); - done(); - }, 50); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 20 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, null) - - var callback = sinon.spy(); - imagekit.deleteCustomMetadataField("fieldId", callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, {}); - done(); - }, 50); - }); - }); - - describe("Error callbacks", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get fields', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.getCustomMetadataFields({}, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 20 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.deleteCustomMetadataField("fieldId", callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - }); -}); - diff --git a/tests/custom-tests/helper-authentication.test.ts b/tests/custom-tests/helper-authentication.test.ts new file mode 100644 index 00000000..4669615e --- /dev/null +++ b/tests/custom-tests/helper-authentication.test.ts @@ -0,0 +1,61 @@ +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateKey: 'private_key_test', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('Helper Authentication Parameters', function () { + it('should return correct authentication parameters with provided token and expire', function () { + const authenticationParameters = client.helper.getAuthenticationParameters('your_token', 1582269249); + + expect(authenticationParameters).toEqual({ + token: 'your_token', + expire: 1582269249, + signature: 'e71bcd6031016b060d349d212e23e85c791decdd', + }); + }); + + it('should return authentication parameters with required properties when no params provided', function () { + const authenticationParameters = client.helper.getAuthenticationParameters(); + + expect(authenticationParameters).toHaveProperty('token'); + expect(authenticationParameters).toHaveProperty('expire'); + expect(authenticationParameters).toHaveProperty('signature'); + + // Token should be a UUID (36 characters with dashes) + expect(authenticationParameters.token).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, + ); + + // Expire should be a number greater than current time + expect(typeof authenticationParameters.expire).toBe('number'); + expect(authenticationParameters.expire).toBeGreaterThan(Math.floor(Date.now() / 1000)); + + // Signature should be a hex string (40 characters for HMAC-SHA1) + expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/); + }); + + it('should handle edge case with expire time 0', function () { + // When expire is 0, it's falsy, so the method uses default expire time + const authenticationParameters = client.helper.getAuthenticationParameters('test-token', 0); + + expect(authenticationParameters.token).toBe('test-token'); + // Since 0 is falsy, it should use the default expire (30 minutes from now) + const expectedExpire = Math.floor(Date.now() / 1000) + 60 * 30; + expect(authenticationParameters.expire).toBeCloseTo(expectedExpire, -1); + expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/); + }); + + it('should handle empty string token', function () { + // When token is empty string, it's falsy, so the method generates a UUID + const authenticationParameters = client.helper.getAuthenticationParameters('', 1582269249); + + // Since '' is falsy, it should generate a UUID + expect(authenticationParameters.token).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, + ); + expect(authenticationParameters.expire).toBe(1582269249); + expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/); + }); +}); diff --git a/tests/custom-tests/url-generation/basic.test.ts b/tests/custom-tests/url-generation/basic.test.ts new file mode 100644 index 00000000..905c8534 --- /dev/null +++ b/tests/custom-tests/url-generation/basic.test.ts @@ -0,0 +1,1336 @@ +import ImageKit from '@imagekit/nodejs'; +import type { SrcOptions } from '../../../src/resources/shared'; + +const client = new ImageKit({ + privateKey: 'My Private API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('URL generation', function () { + it('should return an empty string when src is not provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + } as SrcOptions); + + expect(url).toBe(''); + }); + + it('should return an empty string when src is /', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/', + }); + + expect(url).toBe('https://ik.imagekit.io/test_url_endpoint/'); + }); + + it('should return an empty string when src is invalid', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: 'https://', + }); + + expect(url).toBe(''); + }); + + it('should generate a valid URL when src is provided without transformation', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); + }); + + it('should generate a valid URL when a src is provided without transformation', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: 'https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg', + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); + }); + + it('should generate a valid URL when undefined transformation parameters are provided with path', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/test_path_alt.jpg', + transformationPosition: 'query', + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); + }); + + it('By default transformationPosition should be query', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + { + rotation: 90, + }, + ], + }); + expect(url).toBe('https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90'); + }); + + it('should generate the URL without sdk version', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + transformationPosition: 'path', + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); + }); + + it('should generate the correct URL with a valid src and transformation', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + // Now transformed URL goes into query since transformationPosition is "query". + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should add transformation as query when src has absolute url even if transformationPosition is path', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'path', + src: 'https://my.custom.domain.com/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + // Now transformed URL goes into query since transformationPosition is "query". + expect(url).toBe(`https://my.custom.domain.com/test_path.jpg?tr=h-300,w-400`); + }); + + it('Handle non-default url-endpoint case', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/imagekit_id/new-endpoint/', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + // Now transformed URL goes into query since transformationPosition is "query". + expect(url).toBe(`https://ik.imagekit.io/imagekit_id/new-endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL when the provided path contains multiple leading slashes', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '///test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL when the urlEndpoint is overridden', function () { + const url = client.helper.buildSrc({ + // We do not override urlEndpoint here + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint_alt', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/test_path.jpg', + transformationPosition: 'query', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL with a valid src parameter and transformation', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: 'https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: 'https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg', + transformationPosition: 'query', + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); + }); + + it('should merge query parameters correctly in the generated URL', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: 'https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1', + queryParameters: { t2: 'v2', t3: 'v3' }, + transformation: [ + { + height: '300', + width: '400', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=h-300,w-400`, + ); + }); + + it('should generate the correct URL with chained transformations', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + { + rotation: '90', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90`); + }); + + it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + }, + { + raw: 'rndm_trnsf-abcd', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rndm_trnsf-abcd`); + }); + + it('should generate the correct URL when overlay image transformation is provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + raw: 'l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end`, + ); + }); + + it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + raw: 'l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end`, + ); + }); + + it('should generate the correct URL when border transformation is applied', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: '300', + width: '400', + border: '20_FF0000', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,b-20_FF0000`); + }); + + it('should generate the correct URL when transformation has empty key and value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + raw: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); + }); + + it('should generate the correct URL when an undefined transform is provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + raw: 'undefined-transform-true', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=undefined-transform-true`); + }); + + it('should generate the correct URL when transformation key has an empty value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + defaultImage: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=di-`); + }); + + it("should generate the correct URL when transformation key has '-' as its value", function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + contrastStretch: '-' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=e-contrast`); + }); + + it('should skip transformation parameters that are undefined or null', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + // quality: undefined, // Can't test this due to exactOptionalPropertyTypes + // contrastStretch: null, // Can't test this due to exactOptionalPropertyTypes + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); + }); + + it('should skip transformation parameters that are false', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + contrastStretch: false as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); + }); + + it('should include only the key when transformation value is an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + shadow: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow`); + }); + + it('should include both key and value when transformation parameter value is provided', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + shadow: 'bl-15_st-40_x-10_y-N5', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5`, + ); + }); + + it('should generate the correct URL when trim transformation is set to true as a boolean', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + trim: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); + }); + + it('should generate the correct URL when trim transformation is set to true as a string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + defaultImage: '/test_path.jpg', + trim: 'true' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); + }); + + it('should generate the correct URL for AI background removal when set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackground: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); + }); + + it("should generate the correct URL for AI background removal when 'true' is provided as a string", function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackground: 'true' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); + }); + + it('should not apply AI background removal when value is not true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackground: 'false' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); + }); + + it('should generate the correct URL for external AI background removal when set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackgroundExternal: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); + }); + + it("should generate the correct URL for external AI background removal when 'true' is provided as a string", function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackgroundExternal: 'true' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); + }); + + it('should not apply external AI background removal when value is not true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiRemoveBackgroundExternal: 'false' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); + }); + + it('should generate the correct URL when gradient transformation is provided as a string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + gradient: 'ld-top_from-green_to-00FF0010_sp-1', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1`, + ); + }); + + it('should generate the correct URL when gradient transformation is provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + gradient: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); + }); + + it('should generate the correct URL when gradient transformation is set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + gradient: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); + }); + + it('should generate the correct URL when AI drop shadow transformation is set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiDropShadow: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); + }); + + it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiDropShadow: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); + }); + + it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aiDropShadow: 'az-45', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-az-45`); + }); + + it('should generate the correct URL when shadow transformation is set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + shadow: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); + }); + + it('should generate the correct URL when shadow transformation is provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + shadow: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); + }); + + it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + shadow: 'bl-15_st-40_x-10_y-N5', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow-bl-15_st-40_x-10_y-N5`, + ); + }); + + it('should generate the correct URL when sharpen transformation is set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + sharpen: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); + }); + + it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + sharpen: '' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); + }); + + it('should generate the correct URL when sharpen transformation is provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + sharpen: 10, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen-10`); + }); + + it('should generate the correct URL when unsharpMask transformation is set to true', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + unsharpMask: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); + }); + + it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + unsharpMask: '', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); + }); + + it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + unsharpMask: '2-2-0.8-0.024', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm-2-2-0.8-0.024`); + }); + + it('should generate the correct URL for trim transformation when set to true (boolean)', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + trim: true, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); + }); + + it('should generate the correct URL for trim transformation when provided as an empty string', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + trim: '' as any, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); + }); + + it('should generate the correct URL for trim transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + trim: 5, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-5`); + }); + + // Width parameter tests + it('should generate the correct URL for width transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + width: 400, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); + }); + + it('should generate the correct URL for width transformation when provided with a string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + width: '400', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); + }); + + it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + width: 'iw_div_2', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-iw_div_2`); + }); + + // Height parameter tests + it('should generate the correct URL for height transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + height: 300, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); + }); + + it('should generate the correct URL for height transformation when provided with a string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + height: '300', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); + }); + + it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + height: 'ih_mul_0.5', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-ih_mul_0.5`); + }); + + // AspectRatio parameter tests + it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aspectRatio: '4:3', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3`); + }); + + it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aspectRatio: '4_3', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4_3`); + }); + + it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + aspectRatio: 'iar_div_2', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-iar_div_2`); + }); + + // Background parameter tests + it('should generate the correct URL for background transformation when provided with a solid color', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + background: 'FF0000', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-FF0000`); + }); + + it('should generate the correct URL for background transformation when provided with the blurred option', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + background: 'blurred', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-blurred`); + }); + + it('should generate the correct URL for background transformation when provided with the genfill option', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + background: 'genfill', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-genfill`); + }); + + // Crop parameter tests + it('should generate the correct URL for crop transformation when provided with force value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + crop: 'force', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-force`); + }); + + it('should generate the correct URL for crop transformation when provided with at_max value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + crop: 'at_max', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-at_max`); + }); + + // CropMode parameter tests + it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + cropMode: 'pad_resize', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-pad_resize`); + }); + + it('should generate the correct URL for cropMode transformation when provided with extract value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + cropMode: 'extract', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-extract`); + }); + + // Focus parameter tests + it('should generate the correct URL for focus transformation when provided with a string value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + focus: 'center', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-center`); + }); + + it('should generate the correct URL for focus transformation when face detection is specified', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + focus: 'face', + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-face`); + }); + + // Quality parameter test + it('should generate the correct URL for quality transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + quality: 80, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80`); + }); + + // Coordinate parameters tests + it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + x: 10, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=x-10`); + }); + + it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + y: 20, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=y-20`); + }); + + it('should generate the correct URL for xCenter transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + xCenter: 30, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=xc-30`); + }); + + it('should generate the correct URL for yCenter transformation when provided with a number value', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path1.jpg', + transformation: [ + { + yCenter: 40, + }, + ], + }); + + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=yc-40`); + }); + + it('Including deprecated properties', function () { + // This is just testing how the SDK constructs the URL, not actual valid transformations. + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: 300, + width: 400, + aspectRatio: '4-3', + quality: 40, + crop: 'force', + cropMode: 'extract', + focus: 'left', + format: 'jpeg', + radius: 50, + background: 'A94D34', + border: '5-A94D34', + rotation: 90, + blur: 10, + named: 'some_name', + progressive: true, + lossless: true, + trim: 5, + metadata: true, + colorProfile: true, + defaultImage: '/folder/file.jpg/', + dpr: 3, + sharpen: 10, + unsharpMask: '2-2-0.8-0.024', + contrastStretch: true, + grayscale: true, + shadow: 'bl-15_st-40_x-10_y-N5', + gradient: 'from-red_to-white', + original: true, + raw: 'h-200,w-300,l-image,i-logo.png,l-end', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end`, + ); + }); + + it('should generate the correct URL with many transformations, including video and AI transforms', function () { + // Example test with comprehensive transformations + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + transformationPosition: 'query', + src: '/test_path.jpg', + transformation: [ + { + height: 300, + width: 400, + aspectRatio: '4-3', + quality: 40, + crop: 'force', + cropMode: 'extract', + focus: 'left', + format: 'jpeg', + radius: 50, + background: 'A94D34', + border: '5-A94D34', + rotation: 90, + blur: 10, + named: 'some_name', + progressive: true, + lossless: true, + trim: 5, + metadata: true, + colorProfile: true, + defaultImage: '/folder/file.jpg/', + dpr: 3, + x: 10, + y: 20, + xCenter: 30, + yCenter: 40, + flip: 'h', + opacity: 0.8, + zoom: 2, + // Video transformations + videoCodec: 'h264', + audioCodec: 'aac', + startOffset: 5, + endOffset: 15, + duration: 10, + streamingResolutions: ['1440', '1080'], + // AI transformations + grayscale: true, + aiUpscale: true, + aiRetouch: true, + aiVariation: true, + aiDropShadow: true, + aiChangeBackground: 'prompt-car', + aiEdit: 'prompt-make it vintage', + aiRemoveBackground: true, + contrastStretch: true, + shadow: 'bl-15_st-40_x-10_y-N5', + sharpen: 10, + unsharpMask: '2-2-0.8-0.024', + gradient: 'from-red_to-white', + original: true, + page: '2_4', + raw: 'h-200,w-300,l-image,i-logo.png,l-end', + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-edit-prompt-make it vintage,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end`, + ); + }); +}); diff --git a/tests/custom-tests/url-generation/buildTransformationString.test.ts b/tests/custom-tests/url-generation/buildTransformationString.test.ts new file mode 100644 index 00000000..c738a972 --- /dev/null +++ b/tests/custom-tests/url-generation/buildTransformationString.test.ts @@ -0,0 +1,31 @@ +import ImageKit from '@imagekit/nodejs'; +import type { Transformation } from '../../../src/resources/shared'; + +const client = new ImageKit({ + privateKey: 'My Private API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('buildTransformationString', function () { + it('should return an empty string when no transformations are provided', function () { + const result = client.helper.buildTransformationString([{}] as Transformation[]); + expect(result).toBe(''); + }); + + it('should generate a transformation string for width only', function () { + const result = client.helper.buildTransformationString([{ width: 300 }]); + expect(result).toBe('w-300'); + }); + + it('should generate a transformation string for multiple transformations', function () { + const result = client.helper.buildTransformationString([ + { + overlay: { + type: 'text', + text: 'Hello', + }, + }, + ]); + expect(result).toBe('l-text,i-Hello,l-end'); + }); +}); diff --git a/tests/custom-tests/url-generation/overlay.test.ts b/tests/custom-tests/url-generation/overlay.test.ts new file mode 100644 index 00000000..b287c272 --- /dev/null +++ b/tests/custom-tests/url-generation/overlay.test.ts @@ -0,0 +1,638 @@ +import ImageKit from '@imagekit/nodejs'; +import { safeBtoa } from '../../../src/lib/transformation-utils'; + +const client = new ImageKit({ + privateKey: 'My Private API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('Overlay Transformation Test Cases', function () { + it('Ignore invalid values if text is missing', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'text', + } as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore if type is missing', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: {} as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input (image)', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'image', + } as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input (video)', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'video', + } as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input (subtitle)', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'subtitle', + } as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if color is missing (solidColor)', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'solidColor', + } as any, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Text overlay generates correct URL with encoded overlay text', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'Minimal Text', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent( + 'Minimal Text', + )},l-end/base-image.jpg`, + ); + }); + + it('Image overlay generates correct URL with input logo.png', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'image', + input: 'logo.png', + }, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,l-end/base-image.jpg`); + }); + + it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-video.mp4', + transformation: [ + { + overlay: { + type: 'video', + input: 'play-pause-loop.mp4', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/tr:l-video,i-play-pause-loop.mp4,l-end/base-video.mp4`, + ); + }); + + it('Subtitle overlay generates correct URL with input subtitle.srt', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-video.mp4', + transformation: [ + { + overlay: { + type: 'subtitle', + input: 'subtitle.srt', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/tr:l-subtitle,i-subtitle.srt,l-end/base-video.mp4`, + ); + }); + + it('Solid color overlay generates correct URL with background color FF0000', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + overlay: { + type: 'solidColor', + color: 'FF0000', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-ik_canvas,bg-FF0000,l-end/base-image.jpg`, + ); + }); + + it('Combined overlay transformations generate correct URL including nested overlays', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/test_url_endpoint', + src: '/base-image.jpg', + transformation: [ + { + // Text overlay + overlay: { + type: 'text', + text: 'Every thing', + position: { + x: '10', + y: '20', + focus: 'center', + }, + timing: { + start: 5, + duration: '10', + end: 15, + }, + transformation: [ + { + width: 'bw_mul_0.5', + fontSize: 20, + fontFamily: 'Arial', + fontColor: '0000ff', + innerAlignment: 'left', + padding: 5, + alpha: 7, + typography: 'b', + background: 'red', + radius: 10, + rotation: 'N45', + flip: 'h', + lineHeight: 20, + }, + ], + }, + }, + { + // Image overlay + overlay: { + type: 'image', + input: 'logo.png', + position: { + x: '10', + y: '20', + focus: 'center', + }, + timing: { + start: 5, + duration: '10', + end: 15, + }, + transformation: [ + { + width: 'bw_mul_0.5', + height: 'bh_mul_0.5', + rotation: 'N45', + flip: 'h', + overlay: { + type: 'text', + text: 'Nested text overlay', + }, + }, + ], + }, + }, + { + // Video overlay. Just for URL generation testing, you can't actually overlay a video on an image. + overlay: { + type: 'video', + input: 'play-pause-loop.mp4', + position: { + x: '10', + y: '20', + focus: 'center', + }, + timing: { + start: 5, + duration: '10', + end: 15, + }, + transformation: [ + { + width: 'bw_mul_0.5', + height: 'bh_mul_0.5', + rotation: 'N45', + flip: 'h', + }, + ], + }, + }, + { + // Subtitle overlay. Just for URL generation testing, you can't actually overlay a subtitle on an image. + overlay: { + type: 'subtitle', + input: 'subtitle.srt', + position: { + x: '10', + y: '20', + focus: 'center', + }, + timing: { + start: 5, + duration: '10', + end: 15, + }, + transformation: [ + { + background: 'red', + color: '0000ff', + fontFamily: 'Arial', + fontOutline: '2_A1CCDD50', + fontShadow: 'A1CCDD_3', + }, + ], + }, + }, + { + // Solid color overlay + overlay: { + type: 'solidColor', + color: 'FF0000', + position: { + x: '10', + y: '20', + focus: 'center', + }, + timing: { + start: 5, + duration: '10', + end: 15, + }, + transformation: [ + { + width: 'bw_mul_0.5', + height: 'bh_mul_0.5', + alpha: 0.5, + background: 'red', + gradient: true, + radius: 'max', + }, + ], + }, + }, + ], + }); + + expect(url).toBe( + `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Every%20thing,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-Nested%20text%20overlay,l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,bg-red,color-0000ff,ff-Arial,fol-2_A1CCDD50,fsh-A1CCDD_3,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,al-0.5,bg-red,e-gradient,r-max,l-end/base-image.jpg`, + ); + }); +}); + +describe('Overlay encoding test cases', function () { + it('Nested simple path, should use i instead of ie, handle slash properly', function () { + const url = client.helper.buildSrc({ + // Using a different endpoint here, as we are checking for /demo + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'image', + input: '/customer_logo/nykaa.png', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-image,i-customer_logo@@nykaa.png,l-end/medium_cafe_B1iTdD0C.jpg`, + ); + }); + + it('Nested non-simple path, should use ie instead of i', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'image', + input: '/customer_logo/Ñykaa.png', + }, + }, + ], + }); + + // Buffer.from(decodeURIComponent("Y3VzdG9tZXJfbG9nby%2FDkXlrYWEucG5n"),"base64").toString() = customer_logo/Ñykaa.png + // Exactly what we want + + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXJfbG9nby%2FDkXlrYWEucG5n,l-end/medium_cafe_B1iTdD0C.jpg`, + ); + }); + + it('Simple text overlay, should use i instead of ie', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'Manu', + }, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/demo/tr:l-text,i-Manu,l-end/medium_cafe_B1iTdD0C.jpg`); + }); + + it('Handle slash in fontFamily in case of custom fonts', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'Manu', + transformation: [ + { + fontFamily: 'nested-path/Poppins-Regular_Q15GrYWmL.ttf', + }, + ], + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-text,i-Manu,ff-nested-path@@Poppins-Regular_Q15GrYWmL.ttf,l-end/medium_cafe_B1iTdD0C.jpg`, + ); + }); + + it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'alnum123-._ ', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent( + 'alnum123-._ ', + )},l-end/medium_cafe_B1iTdD0C.jpg`, + ); + }); + + it('Non simple text overlay, should use ie instead of i', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/medium_cafe_B1iTdD0C.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: "Let's use ©, ®, ™, etc", + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-text,ie-TGV0J3MgdXNlIMKpLCDCriwg4oSiLCBldGM%3D,l-end/medium_cafe_B1iTdD0C.jpg`, + ); + }); + + it('Text overlay with explicit plain encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'HelloWorld', + encoding: 'plain', + }, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/demo/tr:l-text,i-HelloWorld,l-end/sample.jpg`); + }); + + it('Text overlay with explicit base64 encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'HelloWorld', + encoding: 'base64', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-text,ie-${encodeURIComponent( + safeBtoa('HelloWorld'), + )},l-end/sample.jpg`, + ); + }); + + it('Image overlay with explicit plain encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.jpg', + transformation: [ + { + overlay: { + type: 'image', + input: '/customer/logo.png', + encoding: 'plain', + }, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/demo/tr:l-image,i-customer@@logo.png,l-end/sample.jpg`); + }); + + it('Image overlay with explicit base64 encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.jpg', + transformation: [ + { + overlay: { + type: 'image', + input: '/customer/logo.png', + encoding: 'base64', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-image,ie-${encodeURIComponent( + safeBtoa('customer/logo.png'), + )},l-end/sample.jpg`, + ); + }); + + it('Video overlay with explicit base64 encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.mp4', + transformation: [ + { + overlay: { + type: 'video', + input: '/path/to/video.mp4', + encoding: 'base64', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-video,ie-${encodeURIComponent( + safeBtoa('path/to/video.mp4'), + )},l-end/sample.mp4`, + ); + }); + + it('Subtitle overlay with explicit plain encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.mp4', + transformation: [ + { + overlay: { + type: 'subtitle', + input: '/sub.srt', + encoding: 'plain', + }, + }, + ], + }); + expect(url).toBe(`https://ik.imagekit.io/demo/tr:l-subtitle,i-sub.srt,l-end/sample.mp4`); + }); + + it('Subtitle overlay with explicit base64 encoding', function () { + const url = client.helper.buildSrc({ + transformationPosition: 'path', + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.mp4', + transformation: [ + { + overlay: { + type: 'subtitle', + input: 'sub.srt', + encoding: 'base64', + }, + }, + ], + }); + expect(url).toBe( + `https://ik.imagekit.io/demo/tr:l-subtitle,ie-${encodeURIComponent( + safeBtoa('sub.srt'), + )},l-end/sample.mp4`, + ); + }); + + it('Avoid double encoding when transformation string is in query params', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo', + src: '/sample.jpg', + transformation: [ + { + overlay: { + type: 'text', + text: 'Minimal Text', + }, + }, + ], + transformationPosition: 'query', + }); + expect(url).toBe(`https://ik.imagekit.io/demo/sample.jpg?tr=l-text,i-Minimal%20Text,l-end`); + }); +}); diff --git a/tests/custom-tests/url-generation/signing.test.ts b/tests/custom-tests/url-generation/signing.test.ts new file mode 100644 index 00000000..1250e4ce --- /dev/null +++ b/tests/custom-tests/url-generation/signing.test.ts @@ -0,0 +1,151 @@ +import ImageKit from '@imagekit/nodejs'; + +/** + * READ ME + * Always test with real account and real private key, by uploading a private file, so that we know the URL is working as expected. + * DO NOT COMMIT ACTUAL PRIVATE KEYS OR SENSITIVE INFORMATION + * Once everything is working, just replace key with `dummy-key` and update assertions to pass test suite. + * Ideally this code would not require any upkeeping. + */ +const client = new ImageKit({ + privateKey: 'dummy-key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('URL Signing', function () { + it('should generate a signed URL when signed is true without expiresIn', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?ik-s=32dbbbfc5f945c0403c71b54c38e76896ef2d6b0', + ); + }); + + it('should generate a signed URL when signed is true with expiresIn', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + signed: true, + expiresIn: 3600, + }); + + // Expect ik-t exist in the URL. We don't assert signature because it will keep changing. + expect(url).toContain('ik-t'); + }); + + it('should generate a signed URL when expiresIn is above 0 and even if signed is false', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + signed: false, + expiresIn: 3600, + }); + + // Expect ik-t exist in the URL. We don't assert signature because it will keep changing. + expect(url).toContain('ik-t'); + }); + + it('Special characters', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/हिन्दी.png', + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?ik-s=3fff2f31da1f45e007adcdbe95f88c8c330e743c', + ); + }); + + it('Text overlay with special characters', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/हिन्दी.png', + transformation: [ + { + overlay: { + type: 'text', + text: 'हिन्दी', + transformation: [ + { + fontColor: 'red', + fontSize: '32', + fontFamily: 'sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf', + }, + ], + }, + }, + ], + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?tr=l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,co-red,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,l-end&ik-s=ac9f24a03080102555e492185533c1ae6bd93fa7', + ); + }); + + it('should generate signed URL with query parameters', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + queryParameters: { + version: '1.0', + cache: 'false', + }, + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?version=1.0&cache=false&ik-s=f2e5a1b8b6a0b03fd63789dfc6413a94acef9fd8', + ); + }); + + it('should generate signed URL with transformations and query parameters', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + transformation: [{ width: 300, height: 200 }], + queryParameters: { + version: '2.0', + }, + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?version=2.0&tr=w-300,h-200&ik-s=601d97a7834b7554f4dabf0d3fc3a219ceeb6b31', + ); + }); + + it('should not sign URL when signed is false', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + signed: false, + }); + + expect(url).toBe('https://ik.imagekit.io/demo/sdk-testing-files/future-search.png'); + expect(url).not.toContain('ik-s='); + expect(url).not.toContain('ik-t='); + }); + + it('transformationPosition as path', function () { + const url = client.helper.buildSrc({ + urlEndpoint: 'https://ik.imagekit.io/demo/', + src: 'sdk-testing-files/future-search.png', + transformation: [{ width: 300, height: 200 }], + transformationPosition: 'path', + queryParameters: { + version: '2.0', + }, + signed: true, + }); + + expect(url).toBe( + 'https://ik.imagekit.io/demo/tr:w-300,h-200/sdk-testing-files/future-search.png?version=2.0&ik-s=dd1ee8f83d019bc59fd57a5fc4674a11eb8a3496', + ); + }); +}); diff --git a/tests/data/index.js b/tests/data/index.js deleted file mode 100644 index 6d7d2f51..00000000 --- a/tests/data/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports.initializationParams = { - publicKey: "test_public_key", - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - privateKey: "test_private_key", - authenticationEndpoint: "http://test/auth" -} \ No newline at end of file diff --git a/tests/data/test_image.jpg b/tests/data/test_image.jpg deleted file mode 100644 index 8102e278..00000000 Binary files a/tests/data/test_image.jpg and /dev/null differ diff --git a/tests/e2e/node-js/index.js b/tests/e2e/node-js/index.js deleted file mode 100644 index 0e411d94..00000000 --- a/tests/e2e/node-js/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const ImageKit = require("imagekit"); - -try { - var imagekit = new ImageKit({ - publicKey: "public", - privateKey: "private", - urlEndpoint: "https://ik.imagekit.io/xyz" - }); - - var url = imagekit.url({ - path: "Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png", - transformation: [{ - width: 100, - raw: "sdfdsf" - }], - transformationPosition: "path" - }) - - if (url === "https://ik.imagekit.io/xyz/tr:w-100,sdfdsf/Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png") { - process.exit(0); - } else { - console.log("Invalid URL", url); - process.exit(1); - } - - -} catch (ex) { - console.log(ex) - process.exit(1); -} - diff --git a/tests/e2e/typescript/index.ts b/tests/e2e/typescript/index.ts deleted file mode 100644 index e46bf8b1..00000000 --- a/tests/e2e/typescript/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import ImageKit from "imagekit"; - -try { - var imagekit = new ImageKit({ - publicKey: "public", - privateKey: "private", - urlEndpoint: "https://ik.imagekit.io/xyz" - }); - - var url = imagekit.url({ - path: "Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png", - transformation: [{ - width: 100, - raw: "sdfdsf" - }], - transformationPosition: "path" - }) - - if (url === "https://ik.imagekit.io/xyz/tr:w-100,sdfdsf/Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png") { - process.exit(0); - } else { - console.log("Invalid URL", url); - process.exit(1); - } - - -} catch (ex) { - console.log(ex) - process.exit(1); -} \ No newline at end of file diff --git a/tests/e2e/typescript/tsconfig.json b/tests/e2e/typescript/tsconfig.json deleted file mode 100644 index 75dcaeac..00000000 --- a/tests/e2e/typescript/tsconfig.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/tests/form.test.ts b/tests/form.test.ts new file mode 100644 index 00000000..c978df14 --- /dev/null +++ b/tests/form.test.ts @@ -0,0 +1,85 @@ +import { multipartFormRequestOptions, createForm } from '@imagekit/nodejs/internal/uploads'; +import { toFile } from '@imagekit/nodejs/core/uploads'; + +describe('form data validation', () => { + test('valid values do not error', async () => { + await multipartFormRequestOptions( + { + body: { + foo: 'foo', + string: 1, + bool: true, + file: await toFile(Buffer.from('some-content')), + blob: new Blob(['Some content'], { type: 'text/plain' }), + }, + }, + fetch, + ); + }); + + test('null', async () => { + await expect(() => + multipartFormRequestOptions( + { + body: { + null: null, + }, + }, + fetch, + ), + ).rejects.toThrow(TypeError); + }); + + test('undefined is stripped', async () => { + const form = await createForm( + { + foo: undefined, + bar: 'baz', + }, + fetch, + ); + expect(form.has('foo')).toBe(false); + expect(form.get('bar')).toBe('baz'); + }); + + test('nested undefined property is stripped', async () => { + const form = await createForm( + { + bar: { + baz: undefined, + }, + }, + fetch, + ); + expect(Array.from(form.entries())).toEqual([]); + + const form2 = await createForm( + { + bar: { + foo: 'string', + baz: undefined, + }, + }, + fetch, + ); + expect(Array.from(form2.entries())).toEqual([['bar[foo]', 'string']]); + }); + + test('nested undefined array item is stripped', async () => { + const form = await createForm( + { + bar: [undefined, undefined], + }, + fetch, + ); + expect(Array.from(form.entries())).toEqual([]); + + const form2 = await createForm( + { + bar: [undefined, 'foo'], + }, + fetch, + ); + expect(Array.from(form2.entries())).toEqual([['bar[]', 'foo']]); + }); +}); diff --git a/tests/helpers/errors.js b/tests/helpers/errors.js deleted file mode 100644 index 464df30c..00000000 --- a/tests/helpers/errors.js +++ /dev/null @@ -1,2 +0,0 @@ -import errors from '../../libs/constants/errorMessages'; -module.exports = { ...errors }; diff --git a/tests/helpers/spies.js b/tests/helpers/spies.js deleted file mode 100644 index 1dbbc24b..00000000 --- a/tests/helpers/spies.js +++ /dev/null @@ -1,11 +0,0 @@ -// packages -import sinon from 'sinon'; -// internal modules -import pHashUtils from "../../utils/phash"; - -// spies -const pHashDistanceSpy = sinon.spy(pHashUtils, 'pHashDistance'); - -module.exports = { - pHashDistanceSpy, -}; diff --git a/tests/index.test.ts b/tests/index.test.ts new file mode 100644 index 00000000..dd3a4f77 --- /dev/null +++ b/tests/index.test.ts @@ -0,0 +1,806 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIPromise } from '@imagekit/nodejs/core/api-promise'; + +import util from 'node:util'; +import ImageKit from '@imagekit/nodejs'; +import { APIUserAbortError } from '@imagekit/nodejs'; +const defaultFetch = fetch; + +describe('instantiate client', () => { + const env = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...env }; + }); + + afterEach(() => { + process.env = env; + }); + + describe('defaultHeaders', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + defaultHeaders: { 'X-My-Default-Header': '2' }, + privateKey: 'My Private Key', + password: 'My Password', + }); + + test('they are used in the request', async () => { + const { req } = await client.buildRequest({ path: '/foo', method: 'post' }); + expect(req.headers.get('x-my-default-header')).toEqual('2'); + }); + + test('can ignore `undefined` and leave the default', async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + headers: { 'X-My-Default-Header': undefined }, + }); + expect(req.headers.get('x-my-default-header')).toEqual('2'); + }); + + test('can be removed with `null`', async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + headers: { 'X-My-Default-Header': null }, + }); + expect(req.headers.has('x-my-default-header')).toBe(false); + }); + }); + describe('logging', () => { + const env = process.env; + + beforeEach(() => { + process.env = { ...env }; + process.env['IMAGE_KIT_LOG'] = undefined; + }); + + afterEach(() => { + process.env = env; + }); + + const forceAPIResponseForClient = async (client: ImageKit) => { + await new APIPromise( + client, + Promise.resolve({ + response: new Response(), + controller: new AbortController(), + requestLogID: 'log_000000', + retryOfRequestLogID: undefined, + startTime: Date.now(), + options: { + method: 'get', + path: '/', + }, + }), + ); + }; + + test('debug logs when log level is debug', async () => { + const debugMock = jest.fn(); + const logger = { + debug: debugMock, + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + + const client = new ImageKit({ + logger: logger, + logLevel: 'debug', + privateKey: 'My Private Key', + password: 'My Password', + }); + + await forceAPIResponseForClient(client); + expect(debugMock).toHaveBeenCalled(); + }); + + test('default logLevel is warn', async () => { + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.logLevel).toBe('warn'); + }); + + test('debug logs are skipped when log level is info', async () => { + const debugMock = jest.fn(); + const logger = { + debug: debugMock, + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + + const client = new ImageKit({ + logger: logger, + logLevel: 'info', + privateKey: 'My Private Key', + password: 'My Password', + }); + + await forceAPIResponseForClient(client); + expect(debugMock).not.toHaveBeenCalled(); + }); + + test('debug logs happen with debug env var', async () => { + const debugMock = jest.fn(); + const logger = { + debug: debugMock, + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + + process.env['IMAGE_KIT_LOG'] = 'debug'; + const client = new ImageKit({ logger: logger, privateKey: 'My Private Key', password: 'My Password' }); + expect(client.logLevel).toBe('debug'); + + await forceAPIResponseForClient(client); + expect(debugMock).toHaveBeenCalled(); + }); + + test('warn when env var level is invalid', async () => { + const warnMock = jest.fn(); + const logger = { + debug: jest.fn(), + info: jest.fn(), + warn: warnMock, + error: jest.fn(), + }; + + process.env['IMAGE_KIT_LOG'] = 'not a log level'; + const client = new ImageKit({ logger: logger, privateKey: 'My Private Key', password: 'My Password' }); + expect(client.logLevel).toBe('warn'); + expect(warnMock).toHaveBeenCalledWith( + 'process.env[\'IMAGE_KIT_LOG\'] was set to "not a log level", expected one of ["off","error","warn","info","debug"]', + ); + }); + + test('client log level overrides env var', async () => { + const debugMock = jest.fn(); + const logger = { + debug: debugMock, + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; + + process.env['IMAGE_KIT_LOG'] = 'debug'; + const client = new ImageKit({ + logger: logger, + logLevel: 'off', + privateKey: 'My Private Key', + password: 'My Password', + }); + + await forceAPIResponseForClient(client); + expect(debugMock).not.toHaveBeenCalled(); + }); + + test('no warning logged for invalid env var level + valid client level', async () => { + const warnMock = jest.fn(); + const logger = { + debug: jest.fn(), + info: jest.fn(), + warn: warnMock, + error: jest.fn(), + }; + + process.env['IMAGE_KIT_LOG'] = 'not a log level'; + const client = new ImageKit({ + logger: logger, + logLevel: 'debug', + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.logLevel).toBe('debug'); + expect(warnMock).not.toHaveBeenCalled(); + }); + }); + + describe('defaultQuery', () => { + test('with null query params given', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + defaultQuery: { apiVersion: 'foo' }, + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo'); + }); + + test('multiple default query params', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + defaultQuery: { apiVersion: 'foo', hello: 'world' }, + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo&hello=world'); + }); + + test('overriding with `undefined`', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + defaultQuery: { hello: 'world' }, + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.buildURL('/foo', { hello: undefined })).toEqual('http://localhost:5000/foo'); + }); + }); + + test('custom fetch', async () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + privateKey: 'My Private Key', + password: 'My Password', + fetch: (url) => { + return Promise.resolve( + new Response(JSON.stringify({ url, custom: true }), { + headers: { 'Content-Type': 'application/json' }, + }), + ); + }, + }); + + const response = await client.get('/foo'); + expect(response).toEqual({ url: 'http://localhost:5000/foo', custom: true }); + }); + + test('explicit global fetch', async () => { + // make sure the global fetch type is assignable to our Fetch type + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + privateKey: 'My Private Key', + password: 'My Password', + fetch: defaultFetch, + }); + }); + + test('custom signal', async () => { + const client = new ImageKit({ + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', + privateKey: 'My Private Key', + password: 'My Password', + fetch: (...args) => { + return new Promise((resolve, reject) => + setTimeout( + () => + defaultFetch(...args) + .then(resolve) + .catch(reject), + 300, + ), + ); + }, + }); + + const controller = new AbortController(); + setTimeout(() => controller.abort(), 200); + + const spy = jest.spyOn(client, 'request'); + + await expect(client.get('/foo', { signal: controller.signal })).rejects.toThrowError(APIUserAbortError); + expect(spy).toHaveBeenCalledTimes(1); + }); + + test('normalized method', async () => { + let capturedRequest: RequestInit | undefined; + const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise => { + capturedRequest = init; + return new Response(JSON.stringify({}), { headers: { 'Content-Type': 'application/json' } }); + }; + + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + privateKey: 'My Private Key', + password: 'My Password', + fetch: testFetch, + }); + + await client.patch('/foo'); + expect(capturedRequest?.method).toEqual('PATCH'); + }); + + describe('baseUrl', () => { + test('trailing slash', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/custom/path/', + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo'); + }); + + test('no trailing slash', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/custom/path', + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo'); + }); + + afterEach(() => { + process.env['IMAGE_KIT_BASE_URL'] = undefined; + }); + + test('explicit option', () => { + const client = new ImageKit({ + baseURL: 'https://example.com', + privateKey: 'My Private Key', + password: 'My Password', + }); + expect(client.baseURL).toEqual('https://example.com'); + }); + + test('env variable', () => { + process.env['IMAGE_KIT_BASE_URL'] = 'https://example.com/from_env'; + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.baseURL).toEqual('https://example.com/from_env'); + }); + + test('empty env variable', () => { + process.env['IMAGE_KIT_BASE_URL'] = ''; // empty + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.baseURL).toEqual('https://api.imagekit.io'); + }); + + test('blank env variable', () => { + process.env['IMAGE_KIT_BASE_URL'] = ' '; // blank + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.baseURL).toEqual('https://api.imagekit.io'); + }); + + test('in request options', () => { + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/option/foo', + ); + }); + + test('in request options overridden by client options', () => { + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + baseURL: 'http://localhost:5000/client', + }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/client/foo', + ); + }); + + test('in request options overridden by env variable', () => { + process.env['IMAGE_KIT_BASE_URL'] = 'http://localhost:5000/env'; + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual( + 'http://localhost:5000/env/foo', + ); + }); + }); + + test('maxRetries option is correctly set', () => { + const client = new ImageKit({ maxRetries: 4, privateKey: 'My Private Key', password: 'My Password' }); + expect(client.maxRetries).toEqual(4); + + // default + const client2 = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client2.maxRetries).toEqual(2); + }); + + describe('withOptions', () => { + test('creates a new client with overridden options', async () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + maxRetries: 3, + privateKey: 'My Private Key', + password: 'My Password', + }); + + const newClient = client.withOptions({ + maxRetries: 5, + baseURL: 'http://localhost:5001/', + }); + + // Verify the new client has updated options + expect(newClient.maxRetries).toEqual(5); + expect(newClient.baseURL).toEqual('http://localhost:5001/'); + + // Verify the original client is unchanged + expect(client.maxRetries).toEqual(3); + expect(client.baseURL).toEqual('http://localhost:5000/'); + + // Verify it's a different instance + expect(newClient).not.toBe(client); + expect(newClient.constructor).toBe(client.constructor); + }); + + test('inherits options from the parent client', async () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + defaultHeaders: { 'X-Test-Header': 'test-value' }, + defaultQuery: { 'test-param': 'test-value' }, + privateKey: 'My Private Key', + password: 'My Password', + }); + + const newClient = client.withOptions({ + baseURL: 'http://localhost:5001/', + }); + + // Test inherited options remain the same + expect(newClient.buildURL('/foo', null)).toEqual('http://localhost:5001/foo?test-param=test-value'); + + const { req } = await newClient.buildRequest({ path: '/foo', method: 'get' }); + expect(req.headers.get('x-test-header')).toEqual('test-value'); + }); + + test('respects runtime property changes when creating new client', () => { + const client = new ImageKit({ + baseURL: 'http://localhost:5000/', + timeout: 1000, + privateKey: 'My Private Key', + password: 'My Password', + }); + + // Modify the client properties directly after creation + client.baseURL = 'http://localhost:6000/'; + client.timeout = 2000; + + // Create a new client with withOptions + const newClient = client.withOptions({ + maxRetries: 10, + }); + + // Verify the new client uses the updated properties, not the original ones + expect(newClient.baseURL).toEqual('http://localhost:6000/'); + expect(newClient.timeout).toEqual(2000); + expect(newClient.maxRetries).toEqual(10); + + // Original client should still have its modified properties + expect(client.baseURL).toEqual('http://localhost:6000/'); + expect(client.timeout).toEqual(2000); + expect(client.maxRetries).not.toEqual(10); + + // Verify URL building uses the updated baseURL + expect(newClient.buildURL('/bar', null)).toEqual('http://localhost:6000/bar'); + }); + }); + + test('with environment variable arguments', () => { + // set options via env var + process.env['IMAGEKIT_PRIVATE_KEY'] = 'My Private Key'; + process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'] = 'My Password'; + const client = new ImageKit(); + expect(client.privateKey).toBe('My Private Key'); + expect(client.password).toBe('My Password'); + }); + + test('with overridden environment variable arguments', () => { + // set options via env var + process.env['IMAGEKIT_PRIVATE_KEY'] = 'another My Private Key'; + process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'] = 'another My Password'; + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + expect(client.privateKey).toBe('My Private Key'); + expect(client.password).toBe('My Password'); + }); +}); + +describe('request building', () => { + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + + describe('custom headers', () => { + test('handles undefined', async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + body: { value: 'hello' }, + headers: { 'X-Foo': 'baz', 'x-foo': 'bar', 'x-Foo': undefined, 'x-baz': 'bam', 'X-Baz': null }, + }); + expect(req.headers.get('x-foo')).toEqual('bar'); + expect(req.headers.get('x-Foo')).toEqual('bar'); + expect(req.headers.get('X-Foo')).toEqual('bar'); + expect(req.headers.get('x-baz')).toEqual(null); + }); + }); +}); + +describe('default encoder', () => { + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password' }); + + class Serializable { + toJSON() { + return { $type: 'Serializable' }; + } + } + class Collection { + #things: T[]; + constructor(things: T[]) { + this.#things = Array.from(things); + } + toJSON() { + return Array.from(this.#things); + } + [Symbol.iterator]() { + return this.#things[Symbol.iterator]; + } + } + for (const jsonValue of [{}, [], { __proto__: null }, new Serializable(), new Collection(['item'])]) { + test(`serializes ${util.inspect(jsonValue)} as json`, async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + body: jsonValue, + }); + expect(req.headers).toBeInstanceOf(Headers); + expect(req.headers.get('content-type')).toEqual('application/json'); + expect(req.body).toBe(JSON.stringify(jsonValue)); + }); + } + + const encoder = new TextEncoder(); + const asyncIterable = (async function* () { + yield encoder.encode('a\n'); + yield encoder.encode('b\n'); + yield encoder.encode('c\n'); + })(); + for (const streamValue of [ + [encoder.encode('a\nb\nc\n')][Symbol.iterator](), + new Response('a\nb\nc\n').body, + asyncIterable, + ]) { + test(`converts ${util.inspect(streamValue)} to ReadableStream`, async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + body: streamValue, + }); + expect(req.headers).toBeInstanceOf(Headers); + expect(req.headers.get('content-type')).toEqual(null); + expect(req.body).toBeInstanceOf(ReadableStream); + expect(await new Response(req.body).text()).toBe('a\nb\nc\n'); + }); + } + + test(`can set content-type for ReadableStream`, async () => { + const { req } = await client.buildRequest({ + path: '/foo', + method: 'post', + body: new Response('a\nb\nc\n').body, + headers: { 'Content-Type': 'text/plain' }, + }); + expect(req.headers).toBeInstanceOf(Headers); + expect(req.headers.get('content-type')).toEqual('text/plain'); + expect(req.body).toBeInstanceOf(ReadableStream); + expect(await new Response(req.body).text()).toBe('a\nb\nc\n'); + }); +}); + +describe('retries', () => { + test('retry on timeout', async () => { + let count = 0; + const testFetch = async ( + url: string | URL | Request, + { signal }: RequestInit = {}, + ): Promise => { + if (count++ === 0) { + return new Promise( + (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))), + ); + } + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + timeout: 10, + fetch: testFetch, + }); + + expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 }); + expect(count).toEqual(2); + expect( + await client + .request({ path: '/foo', method: 'get' }) + .asResponse() + .then((r) => r.text()), + ).toEqual(JSON.stringify({ a: 1 })); + expect(count).toEqual(3); + }); + + test('retry count header', async () => { + let count = 0; + let capturedRequest: RequestInit | undefined; + const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise => { + count++; + if (count <= 2) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After': '0.1', + }, + }); + } + capturedRequest = init; + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + fetch: testFetch, + maxRetries: 4, + }); + + expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 }); + + expect((capturedRequest!.headers as Headers).get('x-stainless-retry-count')).toEqual('2'); + expect(count).toEqual(3); + }); + + test('omit retry count header', async () => { + let count = 0; + let capturedRequest: RequestInit | undefined; + const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise => { + count++; + if (count <= 2) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After': '0.1', + }, + }); + } + capturedRequest = init; + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + fetch: testFetch, + maxRetries: 4, + }); + + expect( + await client.request({ + path: '/foo', + method: 'get', + headers: { 'X-Stainless-Retry-Count': null }, + }), + ).toEqual({ a: 1 }); + + expect((capturedRequest!.headers as Headers).has('x-stainless-retry-count')).toBe(false); + }); + + test('omit retry count header by default', async () => { + let count = 0; + let capturedRequest: RequestInit | undefined; + const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise => { + count++; + if (count <= 2) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After': '0.1', + }, + }); + } + capturedRequest = init; + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + fetch: testFetch, + maxRetries: 4, + defaultHeaders: { 'X-Stainless-Retry-Count': null }, + }); + + expect( + await client.request({ + path: '/foo', + method: 'get', + }), + ).toEqual({ a: 1 }); + + expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count'); + }); + + test('overwrite retry count header', async () => { + let count = 0; + let capturedRequest: RequestInit | undefined; + const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise => { + count++; + if (count <= 2) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After': '0.1', + }, + }); + } + capturedRequest = init; + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + const client = new ImageKit({ + privateKey: 'My Private Key', + password: 'My Password', + fetch: testFetch, + maxRetries: 4, + }); + + expect( + await client.request({ + path: '/foo', + method: 'get', + headers: { 'X-Stainless-Retry-Count': '42' }, + }), + ).toEqual({ a: 1 }); + + expect((capturedRequest!.headers as Headers).get('x-stainless-retry-count')).toEqual('42'); + }); + + test('retry on 429 with retry-after', async () => { + let count = 0; + const testFetch = async ( + url: string | URL | Request, + { signal }: RequestInit = {}, + ): Promise => { + if (count++ === 0) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After': '0.1', + }, + }); + } + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password', fetch: testFetch }); + + expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 }); + expect(count).toEqual(2); + expect( + await client + .request({ path: '/foo', method: 'get' }) + .asResponse() + .then((r) => r.text()), + ).toEqual(JSON.stringify({ a: 1 })); + expect(count).toEqual(3); + }); + + test('retry on 429 with retry-after-ms', async () => { + let count = 0; + const testFetch = async ( + url: string | URL | Request, + { signal }: RequestInit = {}, + ): Promise => { + if (count++ === 0) { + return new Response(undefined, { + status: 429, + headers: { + 'Retry-After-Ms': '10', + }, + }); + } + return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } }); + }; + + const client = new ImageKit({ privateKey: 'My Private Key', password: 'My Password', fetch: testFetch }); + + expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 }); + expect(count).toEqual(2); + expect( + await client + .request({ path: '/foo', method: 'get' }) + .asResponse() + .then((r) => r.text()), + ).toEqual(JSON.stringify({ a: 1 })); + expect(count).toEqual(3); + }); +}); diff --git a/tests/initialization.js b/tests/initialization.js deleted file mode 100644 index 883753e1..00000000 --- a/tests/initialization.js +++ /dev/null @@ -1,65 +0,0 @@ -import chai from "chai"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; - -describe("Initialization checks", function () { - var imagekit = new ImageKit(initializationParams); - - it('should throw error', function () { - try { - new ImageKit({}); - } catch(err) { - expect(err.message).to.be.equal('Missing publicKey during ImageKit initialization'); - } - }); - - it('should throw error', function () { - try { - new ImageKit({ - publicKey: "test_public_key" - }); - } catch(err) { - expect(err.message).to.be.equal('Missing privateKey during ImageKit initialization'); - } - }); - - it('should throw error', function () { - try { - new ImageKit({ - publicKey: "test_public_key", - privateKey: "test_private_key" - }); - } catch(err) { - expect(err.message).to.be.equal('Missing urlEndpoint during ImageKit initialization'); - } - }); - - it('callback', function () { - var imagekit = new ImageKit({ - urlEndpoint: "https://ik.imagekit.io/demo", - publicKey: "test_public_key", - privateKey: "test_private_key" - }); - try { - imagekit.getFileDetails("fileId","wrongCallback"); - } catch(err) { - expect(err.message).to.be.equal("Callback must be a function.") - } - }); - - it('should have options object', function () { - expect(imagekit.options).to.be.an('object'); - }); - - it('should have correctly initialized options object.', function () { - expect(imagekit.options).to.have.property('publicKey').to.be.equal(initializationParams.publicKey); - expect(imagekit.options).to.have.property('urlEndpoint').to.be.equal(initializationParams.urlEndpoint); - expect(imagekit.options).to.have.property('authenticationEndpoint').to.be.equal(initializationParams.authenticationEndpoint); - }); - - it("should have callable functions 'url' and 'upload'", function () { - expect(imagekit.url).to.exist.and.to.be.a('function'); - expect(imagekit.upload).to.exist.and.to.be.a('function'); - }); -}); \ No newline at end of file diff --git a/tests/mediaLibrary.js b/tests/mediaLibrary.js deleted file mode 100644 index 6f9defa7..00000000 --- a/tests/mediaLibrary.js +++ /dev/null @@ -1,1705 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Media library APIs", function () { - describe("Request body check", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}`) - done(); - return [200] - }) - - imagekit.deleteFile(fileId); - }); - - it('Delete single file missing fileId', function (done) { - imagekit.deleteFile(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Delete file versions', function (done) { - var fileId = "23902390239203923"; - var versionId = "versionId" - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}/versions/${versionId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}`) - done(); - return [200] - }) - - imagekit.deleteFileVersion({ - fileId, - versionId - }); - }); - - it('Delete file versions missing fileId', function (done) { - imagekit.deleteFileVersion(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Delete file versions missing versionId', function (done) { - imagekit.deleteFileVersion({ - fileId: "fileId" - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing versionId parameter for this request" - }) - done(); - }); - }); - - it('Bulk add tags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkAddTags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Bulk add tags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkAddTags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove tags', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/removeTags`) - expect(requestBody).to.be.deep.equal({ - fileIds: [fileId, fileId], - tags: ["tag1", "tag2"] - }) - done(); - return [200] - }) - - imagekit.bulkRemoveTags([fileId, fileId], ["tag1", "tag2"]); - }); - - it('Bulk remove tags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkRemoveTags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove tags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkRemoveTags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Bulk remove AITags', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeAITags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/removeAITags`) - expect(requestBody).to.be.deep.equal({ - fileIds: [fileId, fileId], - AITags: ["tag1", "tag2"] - }) - done(); - return [200] - }) - - imagekit.bulkRemoveAITags([fileId, fileId], ["tag1", "tag2"]); - }); - - it('Bulk remove AITags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkRemoveAITags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove AITags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkRemoveAITags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Copy file - default options', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/copy`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/xyz", - destinationPath: "/abc", - includeFileVersions: false - }) - done(); - return [200] - }) - - imagekit.copyFile({ - sourceFilePath: "/xyz", - destinationPath: "/abc" - }); - }); - - it('Copy file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/copy`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/xyz.jpg", - destinationPath: "/abc", - includeFileVersions: true - }) - done(); - return [200] - }) - - imagekit.copyFile({ - sourceFilePath: "/xyz.jpg", - destinationPath: "/abc", - includeFileVersions: true - }); - }); - - it('Copy file invalid folder path', function (done) { - var sourceFilePath = "/file.jpg"; - imagekit.copyFile({ sourceFilePath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy file invalid file path', function (done) { - var destinationPath = "/"; - imagekit.copyFile({ sourceFilePath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFilePath value", - help: "It should be a string like /path/to/file.jpg'" - }) - done(); - }); - }); - - it('Copy file invalid includeFileVersions value', function (done) { - var sourceFilePath = "/sdf.jpg"; - var destinationPath = "/"; - imagekit.copyFile({ sourceFilePath, destinationPath, includeFileVersions: "sdf" }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid includeFileVersions value", - help: "It should be a boolean" - }) - done(); - }); - }); - - it('Move file', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/move`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/abc.jpg", - destinationPath: "/xyz" - }) - done(); - return [200] - }); - - imagekit.moveFile({ sourceFilePath: "/abc.jpg", destinationPath: "/xyz" }); - }); - - it('Move file invalid folder path', function (done) { - var sourceFilePath = "/file.jpg"; - imagekit.moveFile({ sourceFilePath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Move file invalid file path', function (done) { - var destinationPath = "/"; - imagekit.moveFile({ sourceFilePath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFilePath value", - help: "It should be a string like /path/to/file.jpg'" - }) - done(); - }); - }); - - it('Rename file - default purgeCache value', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/rename`) - expect(requestBody).to.be.deep.equal({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: false - }) - done(); - return [200] - }); - - imagekit.renameFile({ - filePath: "/abc.jpg", - newFileName: "test.jpg" - }) - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/rename`) - expect(requestBody).to.be.deep.equal({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: true - }) - done(); - return [200] - }); - - imagekit.renameFile({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: true - }) - }); - - it('Rename file - invalid filePath', function (done) { - imagekit.renameFile({ - filePath: null, - newFileName: "test.jpg" - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for filePath", - help: "Pass the full path of the file. For example - /path/to/file.jpg" - }) - done(); - }); - }); - - it('Rename file - invalid newFileName', function (done) { - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: null, - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for newFileName. It should be a string.", - help: "" - }) - done(); - }); - }); - - it('Rename file - invalid purgeCache', function (done) { - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.pdf", - purgeCache: "sdf" - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for purgeCache. It should be boolean.", - help: "" - }) - done(); - }); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}/restore`) - expect(requestBody).to.be.empty; - done(); - return [200] - }); - - imagekit.restoreFileVersion({ - fileId, - versionId, - }) - }); - - it('Restore file version - missing fileId', function (done) { - imagekit.restoreFileVersion({ - fileId: null, - versionId: "versionId", - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Missing fileId parameter for this request", - help: "" - }) - done(); - }); - }); - - it('Restore file version - missing versionId', function (done) { - imagekit.restoreFileVersion({ - fileId: "fileId", - versionId: null - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Missing versionId parameter for this request", - help: "" - }) - done(); - }); - }); - - it('Copy folder - default options', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/copyFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: false - }) - done(); - return [200] - }); - - imagekit.copyFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - }); - - it('Copy folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/copyFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: true - }) - done(); - return [200] - }); - - imagekit.copyFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: true - }) - }); - - it('Copy folder invalid sourceFolderPath', function (done) { - var destinationPath = "/"; - imagekit.copyFolder({ sourceFolderPath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy folder invalid destinationPath', function (done) { - var sourceFolderPath = "/"; - imagekit.copyFolder({ sourceFolderPath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy folder invalid includeFileVersions', function (done) { - var sourceFolderPath = "/"; - imagekit.copyFolder({ sourceFolderPath, destinationPath: "/sdf", includeFileVersions: "sdf" }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid includeFileVersions value", - help: "It should be a boolean" - }) - done(); - }); - }); - - it('Move folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/moveFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - done(); - return [200] - }); - - imagekit.moveFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - }); - - it('Move folder invalid destinationPath', function (done) { - var sourceFolderPath = "/"; - imagekit.moveFolder({ sourceFolderPath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Move folder invalid sourceFolderPath', function (done) { - var destinationPath = "/"; - imagekit.moveFolder({ sourceFolderPath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Create folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/folder`) - expect(requestBody).to.be.deep.equal({ - folderName: "abc", - parentFolderPath: "/path/to/folder" - }) - done(); - return [200] - }); - - imagekit.createFolder({ - folderName: "abc", - parentFolderPath: "/path/to/folder" - }) - }); - - it('Create folder invalid name', function (done) { - var folderName = ""; - var parentFolderPath = ""; - imagekit.createFolder({ folderName, parentFolderPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid folderName value", - help: "" - }) - done(); - }); - }); - - it('Create folder invalid path', function (done) { - var folderName = "folder1"; - var parentFolderPath = ""; - imagekit.createFolder({ folderName, parentFolderPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid parentFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Delete folder', function (done) { - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/folder`) - expect(requestBody).to.be.deep.equal({ - folderPath: "/path/to/folder", - }) - done(); - return [200] - }); - - imagekit.deleteFolder("/path/to/folder") - }); - - it('Delete folder invalid path', function (done) { - imagekit.deleteFolder(null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid folderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/metadata`) - done() - return [200] - }) - - imagekit.getFileMetadata(fileId); - }); - - it('Get file metadata using fileId missing fileId', function (done) { - imagekit.getFileMetadata(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Pass either fileId or remote URL of the image as first parameter" - }) - done(); - }); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`) - done() - return [200] - }) - - imagekit.getFileDetails(fileId); - }); - - it('Get file details missing fileId', function (done) { - imagekit.getFileDetails(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get all file versions', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/versions`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions`) - done() - return [200] - }) - - imagekit.getFileVersions(fileId); - }); - - it('Get all file versions - missing fileId', function (done) { - imagekit.getFileVersions(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get file versions details', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/versions/${versionId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}`) - done() - return [200] - }) - - imagekit.getFileVersionDetails({ - fileId, - versionId - }); - }); - - it('Get file versions details - missing fileId', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - imagekit.getFileVersionDetails({ - fileId: null, - versionId - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get file versions details - missing versionId', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - imagekit.getFileVersionDetails({ - fileId, - versionId: null - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing versionId parameter for this request" - }) - done(); - }); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100", - extensions: [ - { - name: "google-auto-tagging", - maxTags: 5, - minConfidence: 95 - } - ], - customMetadata: { - SKU: 10 - }, - webhookUrl: "https://some-domain/some-api-id" - } - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`); - expect(requestBody).to.deep.equal(updateData); - done() - }) - - imagekit.updateFileDetails(fileId, updateData); - }); - - - it('Update publish status', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - publish: { - isPublished: false, - }, - }; - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`); - expect(requestBody).to.deep.equal(updateData); - done() - }) - - imagekit.updateFileDetails(fileId, updateData); - }); - - it('Update file details invalid updateData', function (done) { - var fileId = "23902390239203923"; - - imagekit.updateFileDetails(fileId, null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing file update data for this request" - }) - done(); - }); - }); - - it('Update file details missing fileId', function (done) { - var updateData = { - tags: "sdf", - customCoordinates: "10,10,100,100" - } - - imagekit.updateFileDetails(null, updateData, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100, - tags: ["t-shirt", "summer"] - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query({ - skip: listOptions.skip, - limit: listOptions.limit, - tags: listOptions.tags.join(",") - }) - .reply(function (uri, requestBody) { - expect(requestBody).equal("") - done() - return [200] - }) - - imagekit.listFiles(listOptions); - }); - - it('List files empty list options', function (done) { - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(actualQueryParams => { - if (Object.keys(actualQueryParams).length) { - done("query params should have been empty") - } else { - done(); - } - return true; - }) - .reply(function () { - return [200, dummyAPISuccessResponse] - }) - - imagekit.listFiles(); - }); - - it('List files empty invalid options', function (done) { - imagekit.listFiles("invalid", function (err, response) { - expect(err).to.deep.equal({ - message: "Pass a valid JSON list options e.g. {skip: 10, limit: 100}.", - help: "" - }) - done(); - }); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - fileIds: fileIds - }) - done() - }) - - imagekit.bulkDeleteFiles(fileIds); - }); - - it('Bulk file delete by fileids missing fileIds', function (done) { - imagekit.bulkDeleteFiles(null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/${jobId}`) - done(); - return [200] - }) - - imagekit.getBulkJobStatus(jobId); - }); - - it('Get bulk job status missing jobId', function (done) { - imagekit.getBulkJobStatus(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing jobId parameter" - }) - done(); - }); - }); - }); - - describe("Success callbacks", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, null) - - var callback = sinon.spy(); - - imagekit.deleteFile(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, {}); - done(); - }, 50); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get file metadata using remote URL', function (done) { - var url = "https://ik.imagekit.io/demo/image.jpg"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query({ - url: url - }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - imagekit.getFileDetails(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100" - } - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - imagekit.updateFileDetails(fileId, updateData, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100 - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(listOptions) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.listFiles(listOptions, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - - var callback = sinon.spy(); - - imagekit.bulkDeleteFiles(fileIds, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk add tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/addTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.bulkAddTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk remove tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.bulkRemoveTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Copy file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.copyFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Move file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.moveFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.jpg" - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.restoreFileVersion({ - fileId, - versionId - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Copy folder', function (done) { - var sourceFolderPath = "/folder2"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.copyFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Move folder', function (done) { - var sourceFolderPath = "/folder1"; - var destinationPath = "/folder2/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.moveFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getBulkJobStatus(jobId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Create folder', function (done) { - var folderName = "folder1"; - var parentFolderPath = "/"; - - const scope = nock('https://api.imagekit.io') - .post('/v1/folder') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(201, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.createFolder({ folderName, parentFolderPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Delete folder', function (done) { - var folderPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.deleteFolder(folderPath, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - }); - - describe("Error callbacks", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.deleteFile(fileId, callback) - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file metadata using remote URL', function (done) { - var url = "https://ik.imagekit.io/demo/image.jpg"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/metadata`) - .query({ - url - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - imagekit.getFileDetails(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100" - } - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - imagekit.updateFileDetails(fileId, updateData, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100 - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(listOptions) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.listFiles(listOptions, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - - var callback = sinon.spy(); - - imagekit.bulkDeleteFiles(fileIds, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk add tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/addTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.bulkAddTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk remove tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.bulkRemoveTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Copy file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.copyFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Move file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.moveFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.jpg" - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.restoreFileVersion({ - fileId, - versionId - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Copy folder', function (done) { - var sourceFolderPath = "/folder2"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.copyFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Move folder', function (done) { - var sourceFolderPath = "/folder1"; - var destinationPath = "/folder2/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.moveFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getBulkJobStatus(jobId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Create folder', function (done) { - var folderName = "folder1"; - var parentFolderPath = "/"; - - const scope = nock('https://api.imagekit.io') - .post('/v1/folder') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.createFolder({ folderName, parentFolderPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Delete folder', function (done) { - var folderPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.deleteFolder(folderPath, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Rate limit error', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - var responseBody = { - message: "rate limit exceeded" - }; - - var rateLimitHeaders = { - "X-RateLimit-Limit": 10, - "X-RateLimit-Reset": 1000, - "X-RateLimit-Interval": 1000 - } - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(() => { - return [ - 429, - responseBody, - rateLimitHeaders - ] - }) - - imagekit.bulkDeleteFiles(fileIds, function (err, response) { - expect(err).deep.equal({ - ...responseBody, - ...rateLimitHeaders - }) - done(); - }); - }); - }); -}); - diff --git a/tests/path.test.ts b/tests/path.test.ts new file mode 100644 index 00000000..7a5a43c2 --- /dev/null +++ b/tests/path.test.ts @@ -0,0 +1,462 @@ +import { createPathTagFunction, encodeURIPath } from '@imagekit/nodejs/internal/utils/path'; +import { inspect } from 'node:util'; +import { runInNewContext } from 'node:vm'; + +describe('path template tag function', () => { + test('validates input', () => { + const testParams = ['', '.', '..', 'x', '%2e', '%2E', '%2e%2e', '%2E%2e', '%2e%2E', '%2E%2E']; + const testCases = [ + ['/path_params/', '/a'], + ['/path_params/', '/'], + ['/path_params/', ''], + ['', '/a'], + ['', '/'], + ['', ''], + ['a'], + [''], + ['/path_params/', ':initiate'], + ['/path_params/', '.json'], + ['/path_params/', '?beta=true'], + ['/path_params/', '.?beta=true'], + ['/path_params/', '/', '/download'], + ['/path_params/', '-', '/download'], + ['/path_params/', '', '/download'], + ['/path_params/', '.', '/download'], + ['/path_params/', '..', '/download'], + ['/plain/path'], + ]; + + function paramPermutations(len: number): string[][] { + if (len === 0) return []; + if (len === 1) return testParams.map((e) => [e]); + const rest = paramPermutations(len - 1); + return testParams.flatMap((e) => rest.map((r) => [e, ...r])); + } + + // We need to test how %2E is handled, so we use a custom encoder that does no escaping. + const rawPath = createPathTagFunction((s) => s); + + const emptyObject = {}; + const mathObject = Math; + const numberObject = new Number(); + const stringObject = new String(); + const basicClass = new (class {})(); + const classWithToString = new (class { + toString() { + return 'ok'; + } + })(); + + // Invalid values + expect(() => rawPath`/a/${null}/b`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Null is not a valid path parameter\n' + + '/a/null/b\n' + + ' ^^^^', + ); + expect(() => rawPath`/a/${undefined}/b`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Undefined is not a valid path parameter\n' + + '/a/undefined/b\n' + + ' ^^^^^^^^^', + ); + expect(() => rawPath`/a/${emptyObject}/b`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Object is not a valid path parameter\n' + + '/a/[object Object]/b\n' + + ' ^^^^^^^^^^^^^^^', + ); + expect(() => rawPath`?${mathObject}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Math is not a valid path parameter\n' + + '?[object Math]\n' + + ' ^^^^^^^^^^^^^', + ); + expect(() => rawPath`/${basicClass}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Object is not a valid path parameter\n' + + '/[object Object]\n' + + ' ^^^^^^^^^^^^^^', + ); + expect(() => rawPath`/../${''}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value ".." can\'t be safely passed as a path parameter\n' + + '/../\n' + + ' ^^', + ); + expect(() => rawPath`/../${{}}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value ".." can\'t be safely passed as a path parameter\n' + + 'Value of type Object is not a valid path parameter\n' + + '/../[object Object]\n' + + ' ^^ ^^^^^^^^^^^^^^', + ); + + // Valid values + expect(rawPath`/${0}`).toBe('/0'); + expect(rawPath`/${''}`).toBe('/'); + expect(rawPath`/${numberObject}`).toBe('/0'); + expect(rawPath`${stringObject}/`).toBe('/'); + expect(rawPath`/${classWithToString}`).toBe('/ok'); + + // We need to check what happens with cross-realm values, which we might get from + // Jest or other frames in a browser. + + const newRealm = runInNewContext('globalThis'); + expect(newRealm.Object).not.toBe(Object); + + const crossRealmObject = newRealm.Object(); + const crossRealmMathObject = newRealm.Math; + const crossRealmNumber = new newRealm.Number(); + const crossRealmString = new newRealm.String(); + const crossRealmClass = new (class extends newRealm.Object {})(); + const crossRealmClassWithToString = new (class extends newRealm.Object { + toString() { + return 'ok'; + } + })(); + + // Invalid cross-realm values + expect(() => rawPath`/a/${crossRealmObject}/b`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Object is not a valid path parameter\n' + + '/a/[object Object]/b\n' + + ' ^^^^^^^^^^^^^^^', + ); + expect(() => rawPath`?${crossRealmMathObject}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Math is not a valid path parameter\n' + + '?[object Math]\n' + + ' ^^^^^^^^^^^^^', + ); + expect(() => rawPath`/${crossRealmClass}`).toThrow( + 'Path parameters result in path with invalid segments:\n' + + 'Value of type Object is not a valid path parameter\n' + + '/[object Object]\n' + + ' ^^^^^^^^^^^^^^^', + ); + + // Valid cross-realm values + expect(rawPath`/${crossRealmNumber}`).toBe('/0'); + expect(rawPath`${crossRealmString}/`).toBe('/'); + expect(rawPath`/${crossRealmClassWithToString}`).toBe('/ok'); + + const results: { + [pathParts: string]: { + [params: string]: { valid: boolean; result?: string; error?: string }; + }; + } = {}; + + for (const pathParts of testCases) { + const pathResults: Record = {}; + results[JSON.stringify(pathParts)] = pathResults; + for (const params of paramPermutations(pathParts.length - 1)) { + const stringRaw = String.raw({ raw: pathParts }, ...params); + const plainString = String.raw( + { raw: pathParts.map((e) => e.replace(/\./g, 'x')) }, + ...params.map((e) => 'X'.repeat(e.length)), + ); + const normalizedStringRaw = new URL(stringRaw, 'https://example.com').href; + const normalizedPlainString = new URL(plainString, 'https://example.com').href; + const pathResultsKey = JSON.stringify(params); + try { + const result = rawPath(pathParts, ...params); + expect(result).toBe(stringRaw); + // there are no special segments, so the length of the normalized path is + // equal to the length of the normalized plain path. + expect(normalizedStringRaw.length).toBe(normalizedPlainString.length); + pathResults[pathResultsKey] = { + valid: true, + result, + }; + } catch (e) { + const error = String(e); + expect(error).toMatch(/Path parameters result in path with invalid segment/); + // there are special segments, so the length of the normalized path is + // different than the length of the normalized plain path. + expect(normalizedStringRaw.length).not.toBe(normalizedPlainString.length); + pathResults[pathResultsKey] = { + valid: false, + error, + }; + } + } + } + + expect(results).toMatchObject({ + '["/path_params/","/a"]': { + '["x"]': { valid: true, result: '/path_params/x/a' }, + '[""]': { valid: true, result: '/path_params//a' }, + '["%2E%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E%2e" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E%2e/a\n' + + ' ^^^^^^', + }, + '["%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E/a\n' + + ' ^^^', + }, + }, + '["/path_params/","/"]': { + '["x"]': { valid: true, result: '/path_params/x/' }, + '[""]': { valid: true, result: '/path_params//' }, + '["%2e%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2e%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2e%2E/\n' + + ' ^^^^^^', + }, + '["%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2e" can\'t be safely passed as a path parameter\n' + + '/path_params/%2e/\n' + + ' ^^^', + }, + }, + '["/path_params/",""]': { + '[""]': { valid: true, result: '/path_params/' }, + '["x"]': { valid: true, result: '/path_params/x' }, + '["%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E\n' + + ' ^^^', + }, + '["%2E%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E%2e" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E%2e\n' + + ' ^^^^^^', + }, + }, + '["","/a"]': { + '[""]': { valid: true, result: '/a' }, + '["x"]': { valid: true, result: 'x/a' }, + '["%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E" can\'t be safely passed as a path parameter\n%2E/a\n^^^', + }, + '["%2e%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2e%2E" can\'t be safely passed as a path parameter\n' + + '%2e%2E/a\n' + + '^^^^^^', + }, + }, + '["","/"]': { + '["x"]': { valid: true, result: 'x/' }, + '[""]': { valid: true, result: '/' }, + '["%2E%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E%2e" can\'t be safely passed as a path parameter\n' + + '%2E%2e/\n' + + '^^^^^^', + }, + '["."]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "." can\'t be safely passed as a path parameter\n' + + './\n^', + }, + }, + '["",""]': { + '[""]': { valid: true, result: '' }, + '["x"]': { valid: true, result: 'x' }, + '[".."]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value ".." can\'t be safely passed as a path parameter\n' + + '..\n^^', + }, + '["."]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "." can\'t be safely passed as a path parameter\n' + + '.\n^', + }, + }, + '["a"]': {}, + '[""]': {}, + '["/path_params/",":initiate"]': { + '[""]': { valid: true, result: '/path_params/:initiate' }, + '["."]': { valid: true, result: '/path_params/.:initiate' }, + }, + '["/path_params/",".json"]': { + '["x"]': { valid: true, result: '/path_params/x.json' }, + '["."]': { valid: true, result: '/path_params/..json' }, + }, + '["/path_params/","?beta=true"]': { + '["x"]': { valid: true, result: '/path_params/x?beta=true' }, + '[""]': { valid: true, result: '/path_params/?beta=true' }, + '["%2E%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E%2E?beta=true\n' + + ' ^^^^^^', + }, + '["%2e%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2e%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2e%2E?beta=true\n' + + ' ^^^^^^', + }, + }, + '["/path_params/",".?beta=true"]': { + '[".."]': { valid: true, result: '/path_params/...?beta=true' }, + '["x"]': { valid: true, result: '/path_params/x.?beta=true' }, + '[""]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "." can\'t be safely passed as a path parameter\n' + + '/path_params/.?beta=true\n' + + ' ^', + }, + '["%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2e." can\'t be safely passed as a path parameter\n' + + '/path_params/%2e.?beta=true\n' + + ' ^^^^', + }, + }, + '["/path_params/","/","/download"]': { + '["",""]': { valid: true, result: '/path_params///download' }, + '["","x"]': { valid: true, result: '/path_params//x/download' }, + '[".","%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "." can\'t be safely passed as a path parameter\n' + + 'Value "%2e" can\'t be safely passed as a path parameter\n' + + '/path_params/./%2e/download\n' + + ' ^ ^^^', + }, + '["%2E%2e","%2e"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E%2e" can\'t be safely passed as a path parameter\n' + + 'Value "%2e" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E%2e/%2e/download\n' + + ' ^^^^^^ ^^^', + }, + }, + '["/path_params/","-","/download"]': { + '["","%2e"]': { valid: true, result: '/path_params/-%2e/download' }, + '["%2E",".."]': { valid: true, result: '/path_params/%2E-../download' }, + }, + '["/path_params/","","/download"]': { + '["%2E%2e","%2e%2E"]': { valid: true, result: '/path_params/%2E%2e%2e%2E/download' }, + '["%2E",".."]': { valid: true, result: '/path_params/%2E../download' }, + '["","%2E"]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E" can\'t be safely passed as a path parameter\n' + + '/path_params/%2E/download\n' + + ' ^^^', + }, + '["%2E","."]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "%2E." can\'t be safely passed as a path parameter\n' + + '/path_params/%2E./download\n' + + ' ^^^^', + }, + }, + '["/path_params/",".","/download"]': { + '["%2e%2e",""]': { valid: true, result: '/path_params/%2e%2e./download' }, + '["","%2e%2e"]': { valid: true, result: '/path_params/.%2e%2e/download' }, + '["",""]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value "." can\'t be safely passed as a path parameter\n' + + '/path_params/./download\n' + + ' ^', + }, + '["","."]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value ".." can\'t be safely passed as a path parameter\n' + + '/path_params/../download\n' + + ' ^^', + }, + }, + '["/path_params/","..","/download"]': { + '["","%2E"]': { valid: true, result: '/path_params/..%2E/download' }, + '["","x"]': { valid: true, result: '/path_params/..x/download' }, + '["",""]': { + valid: false, + error: + 'Error: Path parameters result in path with invalid segments:\n' + + 'Value ".." can\'t be safely passed as a path parameter\n' + + '/path_params/../download\n' + + ' ^^', + }, + }, + }); + }); +}); + +describe('encodeURIPath', () => { + const testCases: string[] = [ + '', + // Every ASCII character + ...Array.from({ length: 0x7f }, (_, i) => String.fromCharCode(i)), + // Unicode BMP codepoint + 'å', + // Unicode supplementary codepoint + '😃', + ]; + + for (const param of testCases) { + test('properly encodes ' + inspect(param), () => { + const encoded = encodeURIPath(param); + const naiveEncoded = encodeURIComponent(param); + // we should never encode more characters than encodeURIComponent + expect(naiveEncoded.length).toBeGreaterThanOrEqual(encoded.length); + expect(decodeURIComponent(encoded)).toBe(param); + }); + } + + test("leaves ':' intact", () => { + expect(encodeURIPath(':')).toBe(':'); + }); + + test("leaves '@' intact", () => { + expect(encodeURIPath('@')).toBe('@'); + }); +}); diff --git a/tests/phash.js b/tests/phash.js deleted file mode 100644 index 5699a3a7..00000000 --- a/tests/phash.js +++ /dev/null @@ -1,93 +0,0 @@ -import chai from "chai"; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -var imagekit = new ImageKit(initializationParams); - -// helpers -const errors = require('./helpers/errors'); -const spies = require('./helpers/spies'); - -const { expect } = chai; -const { pHashDistanceSpy } = spies; - -const failureHelper = (expectedError, ...params) => { - const { message, help } = expectedError; - const { message: error } = imagekit.pHashDistance(...params); - - expect(error).to.be.equal(`${message}: ${help}`); -}; - -const successHelper = (distance, ...params) => { - const result = imagekit.pHashDistance(...params); - - expect(result).to.be.a('number'); - expect(result).to.equal(distance); - expect(pHashDistanceSpy.calledOnceWithExactly(...params)).to.equal(true); -}; - -const pHash = { - invalidAlphabeticalString: 'INVALIDHEXSTRING', - invalidCharacterString: 'a4a655~!!@94518b', - invalidHexStringLength: '42', - numeric: 2222222222222222, - valid: 'f06830ca9f1e3e90', - // sets - dissimilar: [ - 'a4a65595ac94518b', - '7838873e791f8400', - ], - similar: [ - '2d5ad3936d2e015b', - '2d6ed293db36a4fb', - ], -}; - -describe('Utils > pHash > Distance calculator', () => { - beforeEach(() => { - pHashDistanceSpy.resetHistory(); - }); - - after(() => { - pHashDistanceSpy.resetHistory(); - }); - - context('Failure cases:', () => { - it('Should return error for missing first pHash', () => { - failureHelper(errors.MISSING_PHASH_VALUE, null, pHash.valid); - }); - - it('Should return error for missing second pHash', () => { - failureHelper(errors.MISSING_PHASH_VALUE, pHash.valid); - }); - - it('Should return error for invalid first pHash', () => { - failureHelper(errors.INVALID_PHASH_VALUE, pHash.invalidAlphabeticalString, pHash.valid); - }); - - it('Should return error for invalid second pHash', () => { - failureHelper(errors.INVALID_PHASH_VALUE, pHash.valid, pHash.invalidCharacterString); - }); - - it('Should return error for unequal pHash lengths', () => { - failureHelper(errors.UNEQUAL_STRING_LENGTH, pHash.valid, pHash.invalidHexStringLength); - }); - }); - - context('Success cases:', () => { - it('Should return zero distance between pHash for same image', () => { - successHelper(0, pHash.valid, pHash.valid); - }); - - it('Should return smaller distance between pHash for similar images', () => { - successHelper(17, pHash.similar[0], pHash.similar[1]); - }); - - it('Should return larger distance between pHash for dissimilar images', () => { - successHelper(37, pHash.dissimilar[0], pHash.dissimilar[1]); - }); - - it('Should return distance for non-string but valid hexanumeric pHash', () => { - successHelper(30, pHash.valid, pHash.numeric); - }); - }); -}); \ No newline at end of file diff --git a/tests/response-metadata.js b/tests/response-metadata.js deleted file mode 100644 index 6ca30796..00000000 --- a/tests/response-metadata.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Only checking for one API success and error. Assuing that all API uses same underlying request util - */ - -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -const dummyAPIErrorResponseString = "Internal server error" - -const responseHeaders = { - 'x-request-id': "request-id" -} - -describe("Promise", function () { - it('Success', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse, responseHeaders) - - var response = await imagekit.getPurgeCacheStatus(requestId); - expect(response).to.be.deep.equal(dummyAPISuccessResponse); - expect(response.$ResponseMetadata.statusCode).to.be.equal(200); - expect(response.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders, - 'content-type': 'application/json' - }); - return Promise.resolve(); - }); - - it('Server handled error', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse, responseHeaders) - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch (ex) { - expect(ex).to.be.deep.equal(dummyAPIErrorResponse); - expect(ex.$ResponseMetadata.statusCode).to.be.equal(500); - expect(ex.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders, - 'content-type': 'application/json' - }); - return Promise.resolve(); - } - - return Promise.reject(); - }); - - it('Server unhandled error', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponseString, responseHeaders) - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch (ex) { - expect(ex).to.be.deep.equal({ - help: dummyAPIErrorResponseString - }); - expect(ex.$ResponseMetadata.statusCode).to.be.equal(500); - expect(ex.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders - }); - return Promise.resolve(); - } - - return Promise.reject(); - }); -}); \ No newline at end of file diff --git a/tests/serialization-utils.test.ts b/tests/serialization-utils.test.ts new file mode 100644 index 00000000..4f2ca2ef --- /dev/null +++ b/tests/serialization-utils.test.ts @@ -0,0 +1,109 @@ +import { serializeUploadOptions } from '../src/lib/serialization-utils'; + +describe('serializeUploadOptions', () => { + it('should serialize all special fields correctly while preserving other fields', () => { + const extensions = [ + { name: 'google-auto-tagging', maxTags: 10, minConfidence: 80 }, + { name: 'remove-bg', options: { bg_color: 'white' } }, + ]; + + const customMetadata = { + photographer: 'John Doe', + category: 'nature', + rating: 5, + }; + + const transformation = { + pre: 'w-500,h-300', + post: [ + { type: 'transformation', value: 'w-200,h-150' }, + { type: 'gif-to-video', value: 'q-80' }, + ], + }; + + const input = { + // Special fields that should be serialized + tags: ['nature', 'landscape', 'photography'], + responseFields: ['tags', 'customMetadata', 'isPrivateFile'], + extensions, + customMetadata, + transformation, + + // Regular fields that should remain unchanged + fileName: 'test-image.jpg', + folder: '/photos/2024', + isPrivateFile: false, + useUniqueFileName: true, + description: 'A beautiful landscape photo', + webhookUrl: 'https://example.com/webhook', + }; + + const result = serializeUploadOptions(input); + + // Assert special fields are properly serialized + expect(result['tags']).toBe('nature,landscape,photography'); + expect(result['responseFields']).toBe('tags,customMetadata,isPrivateFile'); + expect(result['extensions']).toBe(JSON.stringify(extensions)); + expect(result['customMetadata']).toBe(JSON.stringify(customMetadata)); + expect(result['transformation']).toBe(JSON.stringify(transformation)); + + // Assert regular fields remain unchanged + expect(result['fileName']).toBe('test-image.jpg'); + expect(result['folder']).toBe('/photos/2024'); + expect(result['isPrivateFile']).toBe(false); + expect(result['useUniqueFileName']).toBe(true); + expect(result['description']).toBe('A beautiful landscape photo'); + expect(result['webhookUrl']).toBe('https://example.com/webhook'); + + // Ensure original object is not modified + expect(input.tags).toEqual(['nature', 'landscape', 'photography']); + expect(input.extensions).toBe(extensions); + expect(input.customMetadata).toBe(customMetadata); + expect(input.transformation).toBe(transformation); + }); + + it('should handle edge cases with null, undefined, and empty values', () => { + const input = { + fileName: 'test.jpg', + + // undefined values + tags: undefined, + transformation: undefined, + + // null values + responseFields: null, + customMetadata: null, + + // empty arrays and objects + extensions: [], + emptyObject: {}, + emptyArray: [], + + // non-special arrays and objects should remain unchanged + regularArray: ['item1', 'item2'], + regularObject: { key: 'value' }, + }; + + const result = serializeUploadOptions(input); + + // undefined values should remain undefined + expect(result['tags']).toBeUndefined(); + expect(result['transformation']).toBeUndefined(); + + // null values should remain null + expect(result['responseFields']).toBeNull(); + expect(result['customMetadata']).toBeNull(); + + // empty arrays for special fields should be serialized + expect(result['extensions']).toBe('[]'); + + // empty arrays/objects for non-special fields should remain unchanged + expect(result['emptyObject']).toEqual({}); + expect(result['emptyArray']).toEqual([]); + expect(result['regularArray']).toEqual(['item1', 'item2']); + expect(result['regularObject']).toEqual({ key: 'value' }); + + // regular field should remain unchanged + expect(result['fileName']).toBe('test.jpg'); + }); +}); diff --git a/tests/stringifyQuery.test.ts b/tests/stringifyQuery.test.ts new file mode 100644 index 00000000..c3193151 --- /dev/null +++ b/tests/stringifyQuery.test.ts @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { ImageKit } from '@imagekit/nodejs'; + +const { stringifyQuery } = ImageKit.prototype as any; + +describe(stringifyQuery, () => { + for (const [input, expected] of [ + [{ a: '1', b: 2, c: true }, 'a=1&b=2&c=true'], + [{ a: null, b: false, c: undefined }, 'a=&b=false'], + [{ 'a/b': 1.28341 }, `${encodeURIComponent('a/b')}=1.28341`], + [ + { 'a/b': 'c/d', 'e=f': 'g&h' }, + `${encodeURIComponent('a/b')}=${encodeURIComponent('c/d')}&${encodeURIComponent( + 'e=f', + )}=${encodeURIComponent('g&h')}`, + ], + ]) { + it(`${JSON.stringify(input)} -> ${expected}`, () => { + expect(stringifyQuery(input)).toEqual(expected); + }); + } + + for (const value of [[], {}, new Date()]) { + it(`${JSON.stringify(value)} -> `, () => { + expect(() => stringifyQuery({ value })).toThrow(`Cannot stringify type ${typeof value}`); + }); + } +}); diff --git a/tests/unit.js b/tests/unit.js deleted file mode 100644 index 61be7331..00000000 --- a/tests/unit.js +++ /dev/null @@ -1,78 +0,0 @@ -import chai from "chai"; -const expect = chai.expect; -import ImageKit from "../index"; - -import urlBuilder from "../libs/url/builder"; - -describe("Unit test cases", function () { - var imagekit = new ImageKit({ - publicKey: "public_key_test", - privateKey: "private_key_test", - urlEndpoint: "https://test-domain.com/test-endpoint" - }); - - it('Authentication params check', function () { - var authenticationParameters = imagekit.getAuthenticationParameters("your_token", 1582269249); - expect(authenticationParameters).to.deep.equal({ - token: 'your_token', - expire: 1582269249, - signature: 'e71bcd6031016b060d349d212e23e85c791decdd' - }) - }); - - it('Authentication params check no params', function () { - var authenticationParameters = imagekit.getAuthenticationParameters(); - expect(authenticationParameters).to.have.property("token"); - expect(authenticationParameters).to.have.property("expire"); - expect(authenticationParameters).to.have.property("signature"); - }); - - it('Signed URL signature without slash default expiry', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - privateKey: "private_key_test", - url: url, - urlEndpoint:"https://test-domain.com/test-endpoint", - expiryTimestamp: "9999999999" - }) - expect(signature).to.be.equal("41b3075c40bc84147eb71b8b49ae7fbf349d0f00") - }); - - it('Signed URL signature with slash default expiry', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - privateKey: "private_key_test", - url: url, - urlEndpoint:"https://test-domain.com/test-endpoint/", - expiryTimestamp: "9999999999" - }) - expect(signature).to.be.equal("41b3075c40bc84147eb71b8b49ae7fbf349d0f00") - }); - - it('Signed URL signature empty', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - }) - expect(signature).to.be.equal("") - }); - - it('pHash distance different', function () { - var pHashDistance = imagekit.pHashDistance("33699c96619cc69e","968e978414fe04ea"); - expect(pHashDistance).to.be.equal(30) - }); - - it('pHash distance similar', function () { - var pHashDistance = imagekit.pHashDistance("63433b3ccf8e1ebe","f5d2226cd9d32b16"); - expect(pHashDistance).to.be.equal(27) - }); - - it('pHash distance similar reverse', function () { - var pHashDistance = imagekit.pHashDistance("f5d2226cd9d32b16","63433b3ccf8e1ebe"); - expect(pHashDistance).to.be.equal(27) - }); - - it('pHash distance same', function () { - var pHashDistance = imagekit.pHashDistance("33699c96619cc69e","33699c96619cc69e"); - expect(pHashDistance).to.be.equal(0) - }); -}); \ No newline at end of file diff --git a/tests/upload.js b/tests/upload.js deleted file mode 100644 index 935c6f18..00000000 --- a/tests/upload.js +++ /dev/null @@ -1,599 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import nock from "nock"; -import fs from "fs"; -import path from "path"; - -function checkFormData({requestBody, boundary, fieldName, fieldValue}) { - return expect(requestBody).include(`${boundary}\r\nContent-Disposition: form-data; name="${fieldName}"\r\n\r\n${fieldValue}`) -} - -const uploadSuccessResponseObj = { - "fileId": "598821f949c0a938d57563bd", - "name": "file1.jpg", - "url": "https://ik.imagekit.io/your_imagekit_id/images/products/file1.jpg", - "thumbnailUrl": "https://ik.imagekit.io/your_imagekit_id/tr:n-media_library_thumbnail/images/products/file1.jpg", - "height": 300, - "width": 200, - "size": 83622, - "filePath": "/images/products/file1.jpg", - "tags": ["t-shirt", "round-neck", "sale2019"], - "isPrivateFile": false, - "customCoordinates": null, - "fileType": "image", - "AITags":[{"name":"Face","confidence":99.95,"source":"aws-auto-tagging"}], - "extensionStatus":{"aws-auto-tagging":"success"} -}; - -describe("File upload custom endpoint", function () { - var imagekit = new ImageKit({ - ...initializationParams, - uploadEndpoint: "https://custom-env.imagekit.io/api/v1/files/upload" - }); - - it('Upload endpoint test case', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://custom-env.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); - done(); - },10); - }); -}); - -describe("File upload", function () { - var imagekit = new ImageKit(initializationParams); - - it('Invalid upload params', function () { - var callback = sinon.spy(); - - imagekit.upload(null, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing data for upload" }, null); - }); - - it('Missing fileName', function () { - const fileOptions = { - file: "https://ik.imagekit.io/remote-url.jpg" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing fileName parameter for upload" }, null); - }); - - it('Missing file', function () { - const fileOptions = { - fileName: "test_file_name", - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); - }); - - it('Full request', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - tags: ["tag1","tag2"], // array handling - isPrivateFile: true, // Boolean handling - useUniqueFileName: "false", // As string - responseFields: ["tags", "metadata"], - extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } - ], - webhookUrl: "https://your-domain/?appId=some-id", - overwriteFile: true, - overwriteAITags: false, - overwriteTags: true, - overwriteCustomMetadata: false, - customMetadata: { - brand: "Nike", - color: "red" - }, - }; - - var callback = sinon.spy(); - var jsonStringifiedExtensions = JSON.stringify(fileOptions.extensions); - const customMetadata = JSON.stringify(fileOptions.customMetadata); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"tags",fieldValue:"tag1,tag2"}); - checkFormData({requestBody,boundary,fieldName:"isPrivateFile",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"useUniqueFileName",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"responseFields",fieldValue:"tags,metadata"}); - checkFormData({requestBody,boundary,fieldName:"extensions",fieldValue:jsonStringifiedExtensions}); - checkFormData({requestBody,boundary,fieldName:"webhookUrl",fieldValue:"https://your-domain/?appId=some-id"}); - checkFormData({requestBody,boundary,fieldName:"overwriteFile",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"overwriteAITags",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"overwriteTags",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"overwriteCustomMetadata",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"customMetadata",fieldValue:customMetadata}); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Buffer file smaller than 10MB', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: fs.readFileSync(path.join(__dirname,"./data/test_image.jpg")) - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - expect(requestBody.length).equal(399064); - done() - }) - - imagekit.upload(fileOptions); - }); - - it('Buffer file larger than 10MB', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: Buffer.alloc(15000000), // static buffer of 15 MB size - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - expect(requestBody.length).equal(15000347); - }) - - imagekit.upload(fileOptions, function (err, result) { - expect(err).to.equal(null) - done(); - }); - }); - - it('Missing useUniqueFileName', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - isPrivateFile: true - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"isPrivateFile",fieldValue:"true"}); - expect(requestBody).to.not.include("useUniqueFileName"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Missing isPrivateFile and useUniqueFileName', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - tags: "tag1,tag2" // as string - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"tags",fieldValue:"tag1,tag2"}); - expect(requestBody).to.not.include("useUniqueFileName"); - expect(requestBody).to.not.include("isPrivateFile"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Bare minimum request', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - expect(requestBody).to.not.include("tags"); - expect(requestBody).to.not.include("useUniqueFileName"); - expect(requestBody).to.not.include("isPrivateFile"); - expect(requestBody).to.not.include("customCoordinates"); - expect(requestBody).to.not.include("responseFields"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Success callback', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); - done(); - },10); - }); - - it('Success using promise', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions) - .then((response, error) => { - expect(response).to.been.deep.equal(uploadSuccessResponseObj) - done(); - }); - }); - - it('Network error', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .replyWithError("Network error occured") - - imagekit.upload(fileOptions, function(err, response) { - expect(err.message).equal("Network error occured"); - done(); - }); - }); - - it('Server side error', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - var error = { - help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(403, error) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, error, null); - done(); - },10); - }); - - it('Server side error promise', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var error = { - help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(403, error) - - imagekit.upload(fileOptions) - .then((response, error) => { - }) - .catch(error => { - expect(error).to.been.deep.equal(error) - done(); - }) - }); - - it("With pre and post transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { pre: "w-100", post: [{ type: "transformation", value: "h-100" }] }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With pre transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { pre: "w-100" }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With post transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { post: [{ type: "transformation", value: "h-100" }] }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("Should return error for an invalid transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: {}, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid transformation parameter. Please include at least pre, post, or both.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid pre transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { pre: "" }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid pre transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation of type abs", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: [{ type: "abs", value: "" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation of type transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: [{ type: "transformation", value: "" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation if it's not an array", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: {} }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("With checks option", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - checks: "'request.folder' : '/'", - }; - - var callback = sinon.spy(); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "checks", fieldValue: fileOptions.checks }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With isPublished option", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - isPublished: false - }; - - var callback = sinon.spy(); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "isPublished", fieldValue: fileOptions.isPublished }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); -}); diff --git a/tests/uploads.test.ts b/tests/uploads.test.ts new file mode 100644 index 00000000..80ccae1b --- /dev/null +++ b/tests/uploads.test.ts @@ -0,0 +1,107 @@ +import fs from 'fs'; +import type { ResponseLike } from '@imagekit/nodejs/internal/to-file'; +import { toFile } from '@imagekit/nodejs/core/uploads'; +import { File } from 'node:buffer'; + +class MyClass { + name: string = 'foo'; +} + +function mockResponse({ url, content }: { url: string; content?: Blob }): ResponseLike { + return { + url, + blob: async () => content || new Blob([]), + }; +} + +describe('toFile', () => { + it('throws a helpful error for mismatched types', async () => { + await expect( + // @ts-expect-error intentionally mismatched type + toFile({ foo: 'string' }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unexpected data type: object; constructor: Object; props: ["foo"]"`, + ); + + await expect( + // @ts-expect-error intentionally mismatched type + toFile(new MyClass()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unexpected data type: object; constructor: MyClass; props: ["name"]"`, + ); + }); + + it('disallows string at the type-level', async () => { + // @ts-expect-error we intentionally do not type support for `string` + // to help people avoid passing a file path + const file = await toFile('contents'); + expect(file.text()).resolves.toEqual('contents'); + }); + + it('extracts a file name from a Response', async () => { + const response = mockResponse({ url: 'https://example.com/my/audio.mp3' }); + const file = await toFile(response); + expect(file.name).toEqual('audio.mp3'); + }); + + it('extracts a file name from a File', async () => { + const input = new File(['foo'], 'input.jsonl'); + const file = await toFile(input); + expect(file.name).toEqual('input.jsonl'); + }); + + it('extracts a file name from a ReadStream', async () => { + const input = fs.createReadStream('tests/uploads.test.ts'); + const file = await toFile(input); + expect(file.name).toEqual('uploads.test.ts'); + }); + + it('does not copy File objects', async () => { + const input = new File(['foo'], 'input.jsonl', { type: 'jsonl' }); + const file = await toFile(input); + expect(file).toBe(input); + expect(file.name).toEqual('input.jsonl'); + expect(file.type).toBe('jsonl'); + }); + + it('is assignable to File and Blob', async () => { + const input = new File(['foo'], 'input.jsonl', { type: 'jsonl' }); + const result = await toFile(input); + const file: File = result; + const blob: Blob = result; + void file, blob; + }); +}); + +describe('missing File error message', () => { + let prevGlobalFile: unknown; + let prevNodeFile: unknown; + beforeEach(() => { + // The file shim captures the global File object when it's first imported. + // Reset modules before each test so we can test the error thrown when it's undefined. + jest.resetModules(); + const buffer = require('node:buffer'); + // @ts-ignore + prevGlobalFile = globalThis.File; + prevNodeFile = buffer.File; + // @ts-ignore + globalThis.File = undefined; + buffer.File = undefined; + }); + afterEach(() => { + // Clean up + // @ts-ignore + globalThis.File = prevGlobalFile; + require('node:buffer').File = prevNodeFile; + jest.resetModules(); + }); + + test('is thrown', async () => { + const uploads = await import('@imagekit/nodejs/core/uploads'); + await expect( + uploads.toFile(mockResponse({ url: 'https://example.com/my/audio.mp3' })), + ).rejects.toMatchInlineSnapshot( + `[Error: \`File\` is not defined as a global, which is required for file uploads.]`, + ); + }); +}); diff --git a/tests/url-generation.js b/tests/url-generation.js deleted file mode 100644 index 689e234e..00000000 --- a/tests/url-generation.js +++ /dev/null @@ -1,446 +0,0 @@ -import chai from "chai"; -const pkg = require("../package.json"); -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import { encodeStringIfRequired, getSignature } from "../libs/url/builder"; -var imagekit = new ImageKit(initializationParams); - -describe("URL generation", function () { - it('no path no src', function () { - const url = imagekit.url({}); - - expect(url).equal(""); - }); - - it('no transformation path', function () { - const url = imagekit.url({ - path: "/test_path.jpg" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); - }); - - it('no transformation src', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - }); - - it('Undefined parameters with path', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - transformation: undefined, - transformationPosition: undefined, - src: undefined, - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - }); - - it('Signed URL', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - signed: true - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?ik-s=e26ca157df99b30b2443d7cb6886fc396fb4c87b`); - }); - - it('Signed URL with expireSeconds', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - signed: true, - expireSeconds: 100 - }); - - expect(url).includes(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - expect(url).includes(`ik-s=`); - }); - - it("Signed URL with é in filename", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/test_é_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_%C3%A9_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with é in filename and path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/aéb/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with é in filename, path and transformation as path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekité,fs-50,l-end/aéb/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit%C3%A9,fs-50,l-end/a%C3%A9b/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekité,fs-50,l-end" }], - transformationPosition: "path", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit%C3%A9,fs-50,l-end/a%C3%A9b/test_%C3%A9_path_alt.jpg?ik-s=${signature}` - ); - }); - - it("Signed URL with é in filename, path and transformation as query", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/aéb/test_é_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekité,fs-50,l-end" }], - transformationPosition: "query", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end&ik-s=${signature}` - ); - }); - - - it('should generate the correct url with path param', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); - }); - - it('should generate the correct url with path param with multiple leading slash', function () { - const url = imagekit.url({ - path: "///test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); - - }); - - it('should generate the correct url with path param with overidden urlEndpoint', function () { - const url = imagekit.url({ - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint_alt", - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/tr:h-300,w-400/test_path.jpg`); - - }); - - it('should generate the correct url with path param with transformationPosition as query', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with src param', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with transformationPosition as query', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with query params properly merged', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", - queryParameters: { t2: "v2", t3: "v3" }, - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=h-300%2Cw-400`); - }) - - - it('should generate the correct chained transformation', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rt": "90" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400:rt-90/test_path.jpg`); - }); - - - it('should generate the correct chained transformation url with new undocumented tranforamtion parameter', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rndm_trnsf": "abcd" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400:rndm_trnsf-abcd/test_path.jpg`); - }); - - it('Overlay image', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end/test_path.jpg`); - }); - - it('Overlay image with slash in path', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end/test_path.jpg`); - }); - - it('Border', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - border: "20_FF0000" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,b-20_FF0000/test_path.jpg`); - }); - - it('e-sharpen - ', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "e-sharpen": "-", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path.jpg`); - }); - - - it('transformation with defaultImage', function () { - const url = imagekit.url({ - path: "/test_path1.jpg", - transformation: [{ - defaultImage: "test_path.jpg", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); - }); - - it('skip transformation if it is undefined or null', function () { - const url = imagekit.url({ - path: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - quality: undefined, - effectContrast: null - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); - }); - - it("Signed URL with ' in filename", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/test_'_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_'_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with ' in filename and path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with ' in filename, path and transformation as path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit',fs-50,l-end/a'b/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekit',fs-50,l-end" }], - transformationPosition: "path", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit',fs-50,l-end/a'b/test_'_path_alt.jpg?ik-s=${signature}` - ); - }); - - it("Signed URL with ' in filename, path and transformation as query", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?tr=l-text%2Ci-Imagekit%27%2Cfs-50%2Cl-end"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekit',fs-50,l-end" }], - transformationPosition: "query", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?tr=l-text%2Ci-Imagekit%27%2Cfs-50%2Cl-end&ik-s=${signature}` - ); - }); - - - it('All combined', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - height: 300, - width: 400, - aspectRatio: '4-3', - quality: 40, - crop: 'force', - cropMode: 'extract', - focus: 'left', - format: 'jpeg', - radius: 50, - bg: "A94D34", - border: "5-A94D34", - rotation: 90, - blur: 10, - named: "some_name", - progressive: true, - lossless: true, - trim: 5, - metadata: true, - colorProfile: true, - defaultImage: "/folder/file.jpg/", //trailing and leading slash case - dpr: 3, - effectSharpen: 10, - effectUSM: "2-2-0.8-0.024", - effectContrast: true, - effectGray: true, - original: true, - effectShadow: 'bl-15_st-40_x-10_y-N5', - effectGradient: 'from-red_to-white', - raw: "h-200,w-300,l-image,i-logo.png,l-end", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast-true,e-grayscale-true,orig-true,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); - }); -}); - - diff --git a/tests/webhook-signature.js b/tests/webhook-signature.js deleted file mode 100644 index 4e3dd4a5..00000000 --- a/tests/webhook-signature.js +++ /dev/null @@ -1,145 +0,0 @@ -import ImageKit from "../index"; -import { expect } from "chai"; - -// Sample webhook data -const WEBHOOK_REQUEST_SAMPLE_SECRET = "whsec_xeO2UNkfKMQnfJf7Q/Qx+fYptL1wabXd"; -const WEBHOOK_REQUEST_SAMPLE_TIMESTAMP = new Date(1655788406333); -const WEBHOOK_REQUEST_SAMPLE_SIGNATURE_HEADER = - "t=1655788406333,v1=d30758f47fcb31e1fa0109d3b3e2a6c623e699aaf1461cba6bd462ef58ea4b31"; -const WEBHOOK_REQUEST_SAMPLE_RAW_BODY = - '{"type":"video.transformation.accepted","id":"58e6d24d-6098-4319-be8d-40c3cb0a402d","created_at":"2022-06-20T11:59:58.461Z","request":{"x_request_id":"fa98fa2e-d6cd-45b4-acf5-bc1d2bbb8ba9","url":"http://ik.imagekit.io/demo/sample-video.mp4?tr=f-webm,q-10","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0"},"data":{"asset":{"url":"http://ik.imagekit.io/demo/sample-video.mp4"},"transformation":{"type":"video-transformation","options":{"video_codec":"vp9","audio_codec":"opus","auto_rotate":true,"quality":10,"format":"webm"}}}}'; -const WEBHOOK_REQUEST_SAMPLE = Object.seal({ - secret: WEBHOOK_REQUEST_SAMPLE_SECRET, - timestamp: WEBHOOK_REQUEST_SAMPLE_TIMESTAMP, - signatureHeader: WEBHOOK_REQUEST_SAMPLE_SIGNATURE_HEADER, - rawBody: WEBHOOK_REQUEST_SAMPLE_RAW_BODY, - body: JSON.parse(WEBHOOK_REQUEST_SAMPLE_RAW_BODY), -}); - -describe("WebhookSignature", function () { - const verify = (new ImageKit(require("./data").initializationParams)).verifyWebhookEvent; - - context("Test Webhook.verify() - Positive cases", () => { - it("Verify with body as string", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const { timestamp, event } = verify( - webhookRequest.rawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - it("Verify with body as Buffer", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const { timestamp, event } = verify( - Buffer.from(webhookRequest.rawBody), - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - it("Verify with body as Uint8Array", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const rawBody = Uint8Array.from(Buffer.from(webhookRequest.rawBody)); - const { timestamp, event } = verify( - rawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - }); - - context("Test WebhookSignature.verify() - Negative cases", () => { - it("Timestamp missing", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = - "v1=b6bc2aa82491c32f1cbef0eb52b7ffffff467ea65a03b5d4ccdcfb9e0941c946"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Timestamp missing"); - } - }); - it("Timestamp invalid", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = - "t=notANumber,v1=b6bc2aa82491c32f1cbef0eb52b7ffffff467ea65a03b5d4ccdcfb9e0941c946"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Timestamp invalid"); - } - }); - it("Signature missing", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = "t=1656326161409"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Signature missing"); - } - }); - it("Incorrect signature - v1 manipulated", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = `t=${webhookRequest.timestamp.getTime()},v1=d66b01d8f1e158d1af7646184716037510ac8ce0a1e70b726a1b698f954785b2`; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - incorrect request body", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const incorrectBody = { hello: "world" }; - const incorrectRawBody = JSON.stringify(incorrectBody); - try { - verify( - incorrectRawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - timestamp manipulated", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const incorrectSignature = webhookRequest.signatureHeader.replace( - `t=${webhookRequest.timestamp.getTime()}`, - `t=${webhookRequest.timestamp.getTime() + 1}` - ); // Correct timestamp replaced with incorrect timestamp - try { - verify( - webhookRequest.rawBody, - incorrectSignature, - webhookRequest.secret - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - different secret", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - try { - verify( - webhookRequest.rawBody, - webhookRequest.signatureHeader, - "A different secret" - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - }); -}); diff --git a/tsc-multi.json b/tsc-multi.json new file mode 100644 index 00000000..384ddac5 --- /dev/null +++ b/tsc-multi.json @@ -0,0 +1,15 @@ +{ + "targets": [ + { + "extname": ".js", + "module": "commonjs", + "shareHelpers": "internal/tslib.js" + }, + { + "extname": ".mjs", + "module": "esnext", + "shareHelpers": "internal/tslib.mjs" + } + ], + "projects": ["tsconfig.build.json"] +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..3d7881a2 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "include": ["dist/src"], + "exclude": [], + "compilerOptions": { + "rootDir": "./dist/src", + "paths": { + "@imagekit/nodejs/*": ["dist/src/*"], + "@imagekit/nodejs": ["dist/src/index.ts"] + }, + "noEmit": false, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "pretty": true, + "sourceMap": true + } +} diff --git a/tsconfig.deno.json b/tsconfig.deno.json new file mode 100644 index 00000000..849e070d --- /dev/null +++ b/tsconfig.deno.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "include": ["dist-deno"], + "exclude": [], + "compilerOptions": { + "rootDir": "./dist-deno", + "lib": ["es2020", "DOM"], + "noEmit": true, + "declaration": true, + "declarationMap": true, + "outDir": "dist-deno", + "pretty": true, + "sourceMap": true + } +} diff --git a/tsconfig.dist-src.json b/tsconfig.dist-src.json new file mode 100644 index 00000000..c550e299 --- /dev/null +++ b/tsconfig.dist-src.json @@ -0,0 +1,11 @@ +{ + // this config is included in the published src directory to prevent TS errors + // from appearing when users go to source, and VSCode opens the source .ts file + // via declaration maps + "include": ["index.ts"], + "compilerOptions": { + "target": "ES2015", + "lib": ["DOM", "DOM.Iterable", "ES2018"], + "moduleResolution": "node" + } +} diff --git a/tsconfig.json b/tsconfig.json index 69e4ceab..0136ffb1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,74 +1,38 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": ["./types"], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ - "resolveJsonModule": true - }, - "include": ["index.ts", "libs/**/*", "utils/*", "test/*", "types/*"], - "exclude": ["node_modules", "dist"] - } \ No newline at end of file + "include": ["src", "tests", "examples"], + "exclude": [], + "compilerOptions": { + "target": "es2020", + "lib": ["es2020"], + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "baseUrl": "./", + "paths": { + "@imagekit/nodejs/*": ["src/*"], + "@imagekit/nodejs": ["src/index.ts"] + }, + "noEmit": true, + + "resolveJsonModule": true, + + "forceConsistentCasingInFileNames": true, + + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "isolatedModules": false, + + "skipLibCheck": true + } +} diff --git a/utils/authorization.ts b/utils/authorization.ts deleted file mode 100644 index 231c1672..00000000 --- a/utils/authorization.ts +++ /dev/null @@ -1,16 +0,0 @@ -import FormData from "form-data"; - -interface RequestOptions { - url: string; - headers?: Record; - method: string; - formData?: FormData; - qs?: Object; - json?: any; - auth?: { - user: string; - pass: string; - }; -} - -export type { RequestOptions }; diff --git a/utils/hamming-distance.d.ts b/utils/hamming-distance.d.ts deleted file mode 100644 index 7fa5d2c3..00000000 --- a/utils/hamming-distance.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "hamming-distance"; diff --git a/utils/phash.ts b/utils/phash.ts deleted file mode 100644 index dc2f3d8d..00000000 --- a/utils/phash.ts +++ /dev/null @@ -1,30 +0,0 @@ -// import packages -import compare from "hamming-distance"; -// import constants -import errors from "../libs/constants/errorMessages"; - -// regexp validator -const hexRegExp = new RegExp(/^[0-9a-fA-F]+$/, "i"); - -const errorHandler = (error: { message: string; help: string }): Error => new Error(`${error.message}: ${error.help}`); - -const pHashDistance = (firstHash: string, secondHash: string): number | Error => { - if (!firstHash || !secondHash) { - return errorHandler(errors.MISSING_PHASH_VALUE); - } - if (!hexRegExp.test(firstHash) || !hexRegExp.test(secondHash)) { - return errorHandler(errors.INVALID_PHASH_VALUE); - } - - const firstHashString = firstHash.toString(); - const secondHashString = secondHash.toString(); - - if (firstHashString.length !== secondHashString.length) { - return errorHandler(errors.UNEQUAL_STRING_LENGTH); - } - - const distance = compare(firstHashString, secondHashString); - return distance; -}; - -export default { pHashDistance }; diff --git a/utils/request.ts b/utils/request.ts deleted file mode 100644 index aa1bf855..00000000 --- a/utils/request.ts +++ /dev/null @@ -1,91 +0,0 @@ -import respond from "./respond"; -import { RequestOptions } from "./authorization"; -import { ImageKitOptions } from "../libs/interfaces"; -import { IKCallback } from "../libs/interfaces/IKCallback"; -import axios, { AxiosError, AxiosHeaders, AxiosRequestConfig, AxiosResponse } from "axios"; - -// constant -const UnknownError: string = "Unknown error occured"; - -export default function request( - requestOptions: RequestOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - - var options: AxiosRequestConfig = { - method: requestOptions.method, - url: requestOptions.url, - auth: { - username: defaultOptions.privateKey || "", - password: "", - }, - maxBodyLength: Infinity, - }; - - if (typeof requestOptions.json === "object") options.data = requestOptions.json; - else if (typeof requestOptions.formData === "object") options.data = requestOptions.formData; - - if (typeof requestOptions.qs === "object") options.params = requestOptions.qs; - if (typeof requestOptions.headers === "object") options.headers = requestOptions.headers; - - axios(options).then((response: AxiosResponse) => { - if (typeof callback !== "function") return; - const { data, status, headers } = response; - const responseMetadata = { - statusCode: status, - headers: (headers as AxiosHeaders).toJSON() - } - let result = data ? data : {} as T; - // define status code and headers as non-enumerable properties on data - Object.defineProperty(result, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - respond(false, result, callback); - }, (error: AxiosError) => { - if (typeof callback !== "function") return; - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - const responseMetadata = { - statusCode: error.response.status, - headers: (error.response.headers as AxiosHeaders).toJSON() - } - - let result = {} as Object; - if (error.response.data && typeof error.response.data === "object") { - result = error.response.data - } else if (error.response.data && typeof error.response.data === "string") { - result = { - help: error.response.data - } - } - - if (error.response.status === 429) { - result = { - ...result, - "X-RateLimit-Limit": parseInt(error.response.headers["x-ratelimit-limit"], 10), - "X-RateLimit-Reset": parseInt(error.response.headers["x-ratelimit-reset"], 10), - "X-RateLimit-Interval": parseInt(error.response.headers["x-ratelimit-interval"], 10), - } - } - // define status code and headers as non-enumerable properties on data - Object.defineProperty(result, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - respond(true, result, callback); - - } else if (error) { - respond(true, error, callback); - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - } else { - respond(true, new Error(UnknownError), callback); - } - }) -} diff --git a/utils/respond.ts b/utils/respond.ts deleted file mode 100644 index 96c359e3..00000000 --- a/utils/respond.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IKCallback } from "../libs/interfaces/IKCallback"; - -export default function respond(isError: boolean, response: any, callback?: IKCallback) { - if (typeof callback === "function") { - if (isError) { - callback(response, null); - } else { - callback(null, response); - } - } -} diff --git a/utils/transformation.ts b/utils/transformation.ts deleted file mode 100644 index 5b89e51a..00000000 --- a/utils/transformation.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - VARIABLES -*/ -import supportedTransforms from "../libs/constants/supportedTransforms"; -import { UrlOptions, TransformationPosition } from "../libs/interfaces"; - -const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = "path"; -const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; - -const CHAIN_TRANSFORM_DELIMITER: string = ":"; -const TRANSFORM_DELIMITER: string = ","; -const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; - -const getDefault = function (): TransformationPosition { - return DEFAULT_TRANSFORMATION_POSITION; -}; - -const addAsQueryParameter = function (options: UrlOptions): boolean { - return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; -}; - -const getTransformKey = function (transform: string): string { - if (!transform) { - return ""; - } - - return supportedTransforms[transform] || supportedTransforms[transform.toLowerCase()] || ""; -}; - -const getChainTransformDelimiter = function (): string { - return CHAIN_TRANSFORM_DELIMITER; -}; - -const getTransformDelimiter = function (): string { - return TRANSFORM_DELIMITER; -}; - -const getTransformKeyValueDelimiter = function (): string { - return TRANSFORM_KEY_VALUE_DELIMITER; -}; - -export default { - getDefault, - addAsQueryParameter, - getTransformKey, - getChainTransformDelimiter, - getTransformDelimiter, - getTransformKeyValueDelimiter, -}; diff --git a/utils/urlFormatter.ts b/utils/urlFormatter.ts deleted file mode 100644 index 96e2b158..00000000 --- a/utils/urlFormatter.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Adds a leading slash to the given string if it does not already start with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with a leading slash if it was missing. - */ -const addLeadingSlash = function (str: string) { - // Check if the input is a string and does not start with a slash - if (typeof str === "string" && str[0] !== "/") { - // Prepend a slash to the string - str = "/" + str; - } - - // Return the processed string - return str; -}; - - -/** - * Removes the leading slash from the given string if it starts with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with the leading slash removed if it was present. - */ -const removeLeadingSlash = function (str: string) { - // Check if the input is a string and starts with a slash - if (typeof str === "string" && str[0] === "/") { - // Remove the leading slash from the string - str = str.substring(1); - } - - // Return the processed string - return str; -}; - - -/** - * Removes the trailing slash from the given string if it ends with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with the trailing slash removed if it was present. - */ -const removeTrailingSlash = function (str: string) { - // Check if the input is a string and ends with a slash - if (typeof str === "string" && str[str.length - 1] === "/") { - // Remove the trailing slash from the string - str = str.substring(0, str.length - 1); - } - - // Return the processed string - return str; -}; - - -/** - * Adds a trailing slash to the given string if it does not already end with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with a trailing slash if it was missing. - */ -const addTrailingSlash = function (str: string) { - // Check if the input is a string and does not end with a slash - if (typeof str === "string" && str[str.length - 1] !== "/") { - // Append a trailing slash to the string - str = str + "/"; - } - - // Return the processed string - return str; -}; - - -export default { addLeadingSlash, removeLeadingSlash, removeTrailingSlash, addTrailingSlash }; diff --git a/utils/webhook-signature.ts b/utils/webhook-signature.ts deleted file mode 100644 index 24028825..00000000 --- a/utils/webhook-signature.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { createHmac } from "crypto"; -import { isNaN } from "lodash"; -import errorMessages from "../libs/constants/errorMessages"; -import type { WebhookEvent } from "../libs/interfaces"; - -/** - * @description Enum for Webhook signature item names - */ -enum SignatureItems { - Timestamp = "t", - V1 = "v1", -} - -const HASH_ALGORITHM = "sha256"; - -/** - * @param timstamp - Webhook request timestamp - * @param payload - Webhook payload as UTF8 encoded string - * @param secret - Webhook secret as UTF8 encoded string - * @returns Hmac with webhook secret as key and `${timestamp}.${payload}` as hash payload. - */ -const computeHmac = ( - timstamp: Date, - payload: string, - secret: string -): string => { - const hashPayload = `${timstamp.getTime()}.${payload}`; - return createHmac(HASH_ALGORITHM, secret).update(hashPayload).digest("hex"); -}; - -/** - * @description Extract items from webhook signature string - */ -const deserializeSignature = ( - signature: string -): { - timestamp: number; - v1: string; -} => { - const items = signature.split(","); - const itemMap = items.map((item) => item.split("=")); // eg. [["t", 1656921250765], ["v1", 'afafafafafaf']] - const timestampString = itemMap.find( - ([key]) => key === SignatureItems.Timestamp - )?.[1]; // eg. 1656921250765 - - // parse timestamp - if (timestampString === undefined) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_TIMESTAMP_MISSING.message - ); - } - const timestamp = parseInt(timestampString, 10); - if (isNaN(timestamp) || timestamp < 0) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_TIMESTAMP_INVALID.message - ); - } - - // parse v1 signature - const v1 = itemMap.find(([key]) => key === SignatureItems.V1)?.[1]; // eg. 'afafafafafaf' - if (v1 === undefined) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_SIGNATURE_MISSING.message - ); - } - - return { timestamp, v1 }; -}; - -/** - * @param payload - Raw webhook request body (Encoded as UTF8 string or Buffer) - * @param signature - Webhook signature as UTF8 encoded strings (Stored in `x-ik-signature` header of the request) - * @param secret - Webhook secret as UTF8 encoded string [Copy from ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks) - * @returns \{ `timstamp`: Verified UNIX epoch timestamp if signature, `event`: Parsed webhook event payload \} - */ -export const verify = ( - payload: string | Uint8Array, - signature: string, - secret: string -): { - timestamp: number; - event: WebhookEvent; -} => { - const { timestamp, v1 } = deserializeSignature(signature); - const payloadAsString: string = - typeof payload === "string" - ? payload - : Buffer.from(payload).toString("utf8"); - const computedHmac = computeHmac( - new Date(timestamp), - payloadAsString, - secret - ); - if (v1 !== computedHmac) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_SIGNATURE_INCORRECT.message - ); - } - return { - timestamp, - event: JSON.parse(payloadAsString) as WebhookEvent, - }; -}; diff --git a/yarn.lock b/yarn.lock index f033c13b..1935915b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,438 +2,199 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/cli@^7.14.5": - version "7.17.10" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.17.10.tgz#5ea0bf6298bb78f3b59c7c06954f9bd1c79d5943" - integrity sha512-OygVO1M2J4yPMNOW9pb+I6kFGpQK77HmG44Oz3hg8xQIl5L/2zq+ZohwAdSaqYgVwM0SfmPHZHphH4wR8qzVYw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.8" - commander "^4.0.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.0.0" - make-dir "^2.1.0" - slash "^2.0.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" - chokidar "^3.4.0" - -"@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== - dependencies: - "@babel/highlight" "^7.16.7" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471" - integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg== - -"@babel/core@^7.14.6", "@babel/core@^7.7.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" - integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.5" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.5" - "@babel/types" "^7.18.4" - convert-source-map "^1.7.0" +"@andrewbranch/untar.js@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz#ba9494f85eb83017c5c855763969caf1d0adea00" + integrity sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw== + +"@arethetypeswrong/cli@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.17.0.tgz#f97f10926b3f9f9eb5117550242d2e06c25cadac" + integrity sha512-xSMW7bfzVWpYw5JFgZqBXqr6PdR0/REmn3DkxCES5N0JTcB0CVgbIynJCvKBFmXaPc3hzmmTrb7+yPDRoOSZdA== + dependencies: + "@arethetypeswrong/core" "0.17.0" + chalk "^4.1.2" + cli-table3 "^0.6.3" + commander "^10.0.1" + marked "^9.1.2" + marked-terminal "^7.1.0" + semver "^7.5.4" + +"@arethetypeswrong/core@0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.17.0.tgz#abb3b5f425056d37193644c2a2de4aecf866b76b" + integrity sha512-FHyhFizXNetigTVsIhqXKGYLpazPS5YNojEPpZEUcBPt9wVvoEbNIvG+hybuBR+pjlRcbyuqhukHZm1fr+bDgA== + dependencies: + "@andrewbranch/untar.js" "^1.0.3" + cjs-module-lexer "^1.2.3" + fflate "^0.8.2" + lru-cache "^10.4.3" + semver "^7.5.4" + typescript "5.6.1-rc" + validate-npm-package-name "^5.0.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.6.tgz#8be77cd77c55baadcc1eae1c33df90ab6d2151d4" + integrity sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.6" + "@babel/parser" "^7.23.6" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.6" + "@babel/types" "^7.23.6" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" - integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== +"@babel/generator@^7.23.6", "@babel/generator@^7.7.2": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" - integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - -"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" - integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" - -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" - integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== - -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-member-expression-to-functions@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" - integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" - -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - -"@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-wrap-function" "^7.16.8" - "@babel/types" "^7.16.8" - -"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" - integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== - dependencies: - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" - integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== - dependencies: - "@babel/types" "^7.18.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== - dependencies: - "@babel/helper-function-name" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" - -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/highlight@^7.16.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" - integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.6.tgz#d03af2ee5fb34691eec0cda90f5ecbb4d4da145a" + integrity sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.6" + "@babel/types" "^7.23.6" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/node@^7.14.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.18.5.tgz#b44a790b8896436908ebcaf4816c1efce73d61cb" - integrity sha512-zv94ESipS2/YKAOJ+/WAfVEzsl9M8UmPZ7Hwx5qVPgytdrgwUPxfi700iR9KO/w5ZhIHyFyvoZtCTSEcQJF8vQ== - dependencies: - "@babel/register" "^7.17.7" - commander "^4.0.1" - core-js "^3.22.1" - node-environment-flags "^1.0.5" - regenerator-runtime "^0.13.4" - v8flags "^3.1.1" - -"@babel/parser@^7.16.7", "@babel/parser@^7.18.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c" - integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" - integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" - integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - -"@babel/plugin-proposal-async-generator-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" - integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" - integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-proposal-class-static-block@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" - integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" - integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" - integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" - integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" - integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" - integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" - -"@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" - integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" - integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-proposal-private-property-in-object@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" - integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" - integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -442,40 +203,26 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-import-assertions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" - integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -484,7 +231,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -498,7 +252,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4": +"@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -526,607 +280,792 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": +"@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" - integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/template@^7.22.15", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5" + integrity sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" + globals "^11.1.0" -"@babel/plugin-transform-arrow-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.3": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" -"@babel/plugin-transform-async-to-generator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" - integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@babel/plugin-transform-block-scoping@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" - integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== -"@babel/plugin-transform-classes@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" - integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" - globals "^11.1.0" + "@cspotcode/source-map-consumer" "0.8.0" -"@babel/plugin-transform-computed-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" - integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + eslint-visitor-keys "^3.3.0" -"@babel/plugin-transform-destructuring@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" - integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== +"@eslint/config-array@^0.19.0": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" + integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" -"@babel/plugin-transform-duplicate-keys@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" - integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== +"@eslint/core@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" + integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@types/json-schema" "^7.0.15" -"@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== +"@eslint/core@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12" + integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@types/json-schema" "^7.0.15" -"@babel/plugin-transform-for-of@^7.18.1": - version "7.18.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" - integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.20.0": + version "9.20.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4" + integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" + integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== + dependencies: + "@eslint/core" "^0.10.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== - dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== -"@babel/plugin-transform-literals@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" - integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== -"@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@babel/plugin-transform-modules-amd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" - integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-dynamic-import-node "^2.3.3" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" -"@babel/plugin-transform-modules-commonjs@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" - integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== +"@jest/create-cache-key-function@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" + integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.18.2" - babel-plugin-dynamic-import-node "^2.3.3" + "@jest/types" "^29.6.3" -"@babel/plugin-transform-modules-systemjs@^7.18.0": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996" - integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" -"@babel/plugin-transform-modules-umd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" - integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + jest-get-type "^29.6.3" -"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" - integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@babel/plugin-transform-new-target@^7.17.12": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931" - integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" -"@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" -"@babel/plugin-transform-parameters@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" - integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-regenerator@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" - integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - regenerator-transform "^0.15.0" - -"@babel/plugin-transform-reserved-words@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" - integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-spread@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" - integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - -"@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-template-literals@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" - integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-typeof-symbol@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" - integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-typescript@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf" - integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-typescript" "^7.17.12" - -"@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/preset-env@^7.14.5": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" - integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-async-generator-functions" "^7.17.12" - "@babel/plugin-proposal-class-properties" "^7.17.12" - "@babel/plugin-proposal-class-static-block" "^7.18.0" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.17.12" - "@babel/plugin-proposal-json-strings" "^7.17.12" - "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.18.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-private-methods" "^7.17.12" - "@babel/plugin-proposal-private-property-in-object" "^7.17.12" - "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.17.12" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.17.12" - "@babel/plugin-transform-async-to-generator" "^7.17.12" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.17.12" - "@babel/plugin-transform-classes" "^7.17.12" - "@babel/plugin-transform-computed-properties" "^7.17.12" - "@babel/plugin-transform-destructuring" "^7.18.0" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.17.12" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.18.1" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.17.12" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.18.0" - "@babel/plugin-transform-modules-commonjs" "^7.18.2" - "@babel/plugin-transform-modules-systemjs" "^7.18.0" - "@babel/plugin-transform-modules-umd" "^7.18.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" - "@babel/plugin-transform-new-target" "^7.17.12" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.18.0" - "@babel/plugin-transform-reserved-words" "^7.17.12" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.17.12" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.18.2" - "@babel/plugin-transform-typeof-symbol" "^7.17.12" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.2" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.22.1" - semver "^6.3.0" +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@babel/preset-typescript@^7.14.5": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" - integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-typescript" "^7.17.12" - -"@babel/register@^7.14.5", "@babel/register@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" - integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.5" - source-map-support "^0.5.16" - -"@babel/runtime@^7.8.4": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" - integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" - integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.18.5" - "@babel/types" "^7.18.4" - debug "^4.1.0" - globals "^11.1.0" +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.4.4": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" +"@pkgr/core@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" + integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" -"@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@sinonjs/commons" "^3.0.0" -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": - version "2.1.8-no-fsevents.3" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" - integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== +"@stablelib/base64@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-1.0.1.tgz#bdfc1c6d3a62d7a3b7bbc65b6cce1bb4561641be" + integrity sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ== + +"@swc/core-darwin-arm64@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.16.tgz#2cd45d709ce76d448d96bf8d0006849541436611" + integrity sha512-UOCcH1GvjRnnM/LWT6VCGpIk0OhHRq6v1U6QXuPt5wVsgXnXQwnf5k3sG5Cm56hQHDvhRPY6HCsHi/p0oek8oQ== + +"@swc/core-darwin-x64@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.4.16.tgz#a5bc7d8b1dd850adb0bb95c6b5c742b92201fd01" + integrity sha512-t3bgqFoYLWvyVtVL6KkFNCINEoOrIlyggT/kJRgi1y0aXSr0oVgcrQ4ezJpdeahZZ4N+Q6vT3ffM30yIunELNA== + +"@swc/core-linux-arm-gnueabihf@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.16.tgz#961744908ee5cbb79bc009dcf58cc8b831111f38" + integrity sha512-DvHuwvEF86YvSd0lwnzVcjOTZ0jcxewIbsN0vc/0fqm9qBdMMjr9ox6VCam1n3yYeRtj4VFgrjeNFksqbUejdQ== + +"@swc/core-linux-arm64-gnu@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.16.tgz#43713be3f26757d82d2745dc25f8b63400e0a3d0" + integrity sha512-9Uu5YlPbyCvbidjKtYEsPpyZlu16roOZ5c2tP1vHfnU9bgf5Tz5q5VovSduNxPHx+ed2iC1b1URODHvDzbbDuQ== + +"@swc/core-linux-arm64-musl@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.16.tgz#394a7d030f3a61902bd3947bb9d70d26d42f3c81" + integrity sha512-/YZq/qB1CHpeoL0eMzyqK5/tYZn/rzKoCYDviFU4uduSUIJsDJQuQA/skdqUzqbheOXKAd4mnJ1hT04RbJ8FPQ== + +"@swc/core-linux-x64-gnu@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.16.tgz#71eb108b784f9d551ee8a35ebcdaed972f567981" + integrity sha512-UUjaW5VTngZYDcA8yQlrFmqs1tLi1TxbKlnaJwoNhel9zRQ0yG1YEVGrzTvv4YApSuIiDK18t+Ip927bwucuVQ== + +"@swc/core-linux-x64-musl@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.16.tgz#10dbaedb4e3dfc7268e3a9a66ad3431471ef035b" + integrity sha512-aFhxPifevDTwEDKPi4eRYWzC0p/WYJeiFkkpNU5Uc7a7M5iMWPAbPFUbHesdlb9Jfqs5c07oyz86u+/HySBNPQ== + +"@swc/core-win32-arm64-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.16.tgz#80247adff6c245ff32b44d773c1a148858cd655f" + integrity sha512-bTD43MbhIHL2s5QgCwyleaGwl96Gk/scF2TaVKdUe4QlJCDV/YK9h5oIBAp63ckHtE8GHlH4c8dZNBiAXn4Org== + +"@swc/core-win32-ia32-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.16.tgz#e540afc3ccf3224267b4ddfb408f9d9737984686" + integrity sha512-/lmZeAN/qV5XbK2SEvi8e2RkIg8FQNYiSA8y2/Zb4gTUMKVO5JMLH0BSWMiIKMstKDPDSxMWgwJaQHF8UMyPmQ== + +"@swc/core-win32-x64-msvc@1.4.16": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.16.tgz#f880939fca32c181adfe7e3abd2b6b7857bd3489" + integrity sha512-BPAfFfODWXtUu6SwaTTftDHvcbDyWBSI/oanUeRbQR5vVWkXoQ3cxLTsDluc3H74IqXS5z1Uyoe0vNo2hB1opA== + +"@swc/core@^1.3.102": + version "1.4.16" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.4.16.tgz#d175bae2acfecd53bcbd4293f1fba5ec316634a0" + integrity sha512-Xaf+UBvW6JNuV131uvSNyMXHn+bh6LyKN4tbv7tOUFQpXyz/t9YWRE04emtlUW9Y0qrm/GKFCbY8n3z6BpZbTA== + dependencies: + "@swc/counter" "^0.1.2" + "@swc/types" "^0.1.5" + optionalDependencies: + "@swc/core-darwin-arm64" "1.4.16" + "@swc/core-darwin-x64" "1.4.16" + "@swc/core-linux-arm-gnueabihf" "1.4.16" + "@swc/core-linux-arm64-gnu" "1.4.16" + "@swc/core-linux-arm64-musl" "1.4.16" + "@swc/core-linux-x64-gnu" "1.4.16" + "@swc/core-linux-x64-musl" "1.4.16" + "@swc/core-win32-arm64-msvc" "1.4.16" + "@swc/core-win32-ia32-msvc" "1.4.16" + "@swc/core-win32-x64-msvc" "1.4.16" + +"@swc/counter@^0.1.2", "@swc/counter@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== -"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@swc/jest@^0.2.29": + version "0.2.36" + resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.36.tgz#2797450a30d28b471997a17e901ccad946fe693e" + integrity sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw== dependencies: - type-detect "4.0.8" + "@jest/create-cache-key-function" "^29.7.0" + "@swc/counter" "^0.1.3" + jsonc-parser "^3.2.0" -"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@swc/types@^0.1.5": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.6.tgz#2f13f748995b247d146de2784d3eb7195410faba" + integrity sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg== dependencies: - "@sinonjs/commons" "^1.7.0" + "@swc/counter" "^0.1.3" -"@sinonjs/samsam@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" - integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== - dependencies: - "@sinonjs/commons" "^1.6.0" - lodash.get "^4.4.2" - type-detect "^4.0.8" +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== -"@sinonjs/text-encoding@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" - integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== -"@types/caseless@*": - version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" - integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@types/chai@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04" - integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ== +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" -"@types/lodash@^4.14.170": - version "4.14.182" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" - integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" -"@types/mocha@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" -"@types/node@*": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" - integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.4" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.4.tgz#ec2c06fed6549df8bc0eb4615b683749a4a92e1b" + integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== + dependencies: + "@babel/types" "^7.20.7" -"@types/node@^15.12.2": - version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" - integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/request@^2.48.5": - version "2.48.8" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.8.tgz#0b90fde3b655ab50976cb8c5ac00faca22f5a82c" - integrity sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ== +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: - "@types/caseless" "*" "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" -"@types/sinon@^10.0.12": - version "10.0.12" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.12.tgz#fb7009ea71f313a9da4644ba73b94e44d6b84f7f" - integrity sha512-uWf4QJ4oky/GckJ1MYQxU52cgVDcXwBhDkpvLbi4EKoLPqLE4MOH6T/ttM33l3hi0oZ882G6oIzWv/oupRYSxQ== +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: - "@types/sinonjs__fake-timers" "*" + "@types/istanbul-lib-coverage" "*" -"@types/sinonjs__fake-timers@*": - version "8.1.2" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e" - integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" -"@types/tough-cookie@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/jest@^29.4.0": + version "29.5.11" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c" + integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@types/node@*": + version "20.10.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" + integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== + dependencies: + undici-types "~5.26.4" -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== +"@types/node@^20.17.6": + version "20.19.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.11.tgz#728cab53092bd5f143beed7fbba7ba99de3c16c4" + integrity sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow== dependencies: - debug "4" + undici-types "~6.21.0" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz#62f1befe59647524994e89de4516d8dcba7a850a" + integrity sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/type-utils" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/parser@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.1.tgz#e9b0ccf30d37dde724ee4d15f4dbc195995cce1b" + integrity sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q== + dependencies: + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz#1eb52e76878f545e4add142e0d8e3e97e7aa443b" + integrity sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + +"@typescript-eslint/type-utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz#be0f438fb24b03568e282a0aed85f776409f970c" + integrity sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA== + dependencies: + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + debug "^4.3.4" + ts-api-utils "^2.0.1" + +"@typescript-eslint/types@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.1.tgz#478ed6f7e8aee1be7b63a60212b6bffe1423b5d4" + integrity sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ== + +"@typescript-eslint/typescript-estree@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz#37792fe7ef4d3021c7580067c8f1ae66daabacdf" + integrity sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.1.tgz#5628ea0393598a0b2f143d0fc6d019f0dee9dd14" + integrity sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + +"@typescript-eslint/visitor-keys@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz#6742b0e3ba1e0c1e35bdaf78c03e759eb8dd8e75" + integrity sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw== + dependencies: + "@typescript-eslint/types" "8.31.1" + eslint-visitor-keys "^4.2.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +acorn@^8.4.1: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== aggregate-error@^3.0.0: version "3.1.0" @@ -1136,21 +1075,40 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-escapes@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" + integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== + dependencies: + environment "^1.0.0" ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1165,25 +1123,28 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -anymatch@~3.1.1, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" -append-transform@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" - integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== - dependencies: - default-require-extensions "^3.0.0" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" @@ -1197,87 +1158,71 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -argv@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" - integrity sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw== - -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -axios@^1.6.5: - version "1.6.6" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.6.tgz#878db45401d91fe9e53aed8ac962ed93bde8dd1c" - integrity sha512-XZLZDFfXKM9U/Y/B4nNynfCRUqNyVZ4sBC/n9GDRCkq9vd2mIvKjKKsbIh1WPmHmNbg6ND7cTBY3Y2+u1G3/2Q== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: - object.assign "^4.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" - semver "^6.1.1" + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" - core-js-compat "^3.21.0" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" - -babel-plugin-replace-ts-export-assignment@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-replace-ts-export-assignment/-/babel-plugin-replace-ts-export-assignment-0.0.2.tgz#927a30ba303fcf271108980a8d4f80a693e1d53f" - integrity sha512-BiTEG2Ro+O1spuheL5nB289y37FFmz0ISE6GjpNCG2JuA/WNcuEHSYw01+vN8quGf208sID3FnZFDwVyqX18YQ== + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1286,80 +1231,70 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== dependencies: - fill-range "^7.0.1" + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" -browserslist@^4.20.2, browserslist@^4.20.4: - version "4.21.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.0.tgz#7ab19572361a140ecd1e023e2c1ed95edda0cefe" - integrity sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA== +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: - caniuse-lite "^1.0.30001358" - electron-to-chromium "^1.4.164" - node-releases "^2.0.5" - update-browserslist-db "^1.0.0" + node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -caching-transform@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" - integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== - dependencies: - hasha "^5.0.0" - make-dir "^3.0.0" - package-hash "^4.0.0" - write-file-atomic "^3.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001358: - version "1.0.30001358" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001358.tgz#473d35dabf5e448b463095cab7924e96ccfb8c00" - integrity sha512-hvp8PSRymk85R20bsDra7ZTCpSVGN/PAz9pSAjPSjKC+rNmnUk5vCRgJwiTT/O4feQ/yu/drvZYpKxxhbFuChw== - -chai@^4.2.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - loupe "^2.3.1" - pathval "^1.1.1" - type-detect "^4.0.5" - -chalk@^2.0.0: +caniuse-lite@^1.0.30001565: + version "1.0.30001570" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca" + integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1368,7 +1303,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1376,54 +1311,56 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== - -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - -chokidar@^3.4.0: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + +cjs-module-lexer@^1.2.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cli-highlight@^2.1.11: + version "2.1.11" + resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" + integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg== + dependencies: + chalk "^4.0.0" + highlight.js "^10.7.1" + mz "^2.4.0" + parse5 "^5.1.1" + parse5-htmlparser2-tree-adapter "^6.0.0" + yargs "^16.0.0" + +cli-table3@^0.6.3, cli-table3@^0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== dependencies: string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" cliui@^7.0.2: version "7.0.4" @@ -1434,25 +1371,24 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" -codecov@^3.8.0: - version "3.8.3" - resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.3.tgz#9c3e364b8a700c597346ae98418d09880a3fdbe7" - integrity sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA== - dependencies: - argv "0.0.2" - ignore-walk "3.0.4" - js-yaml "3.14.1" - teeny-request "7.1.1" - urlgrey "1.0.0" +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -1478,266 +1414,352 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.6, combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concurrently@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.5.1.tgz#4518c67f7ac680cf5c34d5adf399a2a2047edc8c" - integrity sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag== - dependencies: - chalk "^4.1.0" - date-fns "^2.16.1" - lodash "^4.17.21" - rxjs "^6.6.3" - spawn-command "^0.0.2-1" - supports-color "^8.1.0" - tree-kill "^1.2.2" - yargs "^16.2.0" - -convert-source-map@^1.1.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.23.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.2.tgz#5cbf8a9c8812d665392845b85ae91b5bcc7b615c" - integrity sha512-lrgZvxFwbQp9v7E8mX0rJ+JX7Bvh4eGULZXA1IAyjlsnWvCdw6TF8Tg6xtaSUSJMrSrMaLdpmk+V54LM1dvfOA== - dependencies: - browserslist "^4.20.4" - semver "7.0.0" - -core-js@^3.22.1: - version "3.23.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.2.tgz#e07a60ca8b14dd129cabdc3d2551baf5a01c76f0" - integrity sha512-ELJOWxNrJfOH/WK4VJ3Qd+fOqZuOuDNDJz0xG6Bt4mGg2eO/UT9CljCrbqDGovjLKUrGajEEBcoTOc0w+yBYeQ== - -cross-spawn@^7.0.0, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.3, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -date-fns@^2.16.1: - version "2.28.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" - integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== - -debug@4, debug@^4.1.0, debug@^4.1.1: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@^4.3.4, debug@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -default-require-extensions@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" - integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== - dependencies: - strip-bom "^4.0.0" +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== -diff@^4.0.2: +diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -electron-to-chromium@^1.4.164: - version "1.4.167" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.167.tgz#72424aebc85df12c5331d37b1bcfd1ae01322c55" - integrity sha512-lPHuHXBwpkr4RcfaZBKm6TKOWG/1N9mVggUpP4fY3l1JIUU2x4fkM8928smYdZ5lF+6KCTAxo1aK9JmqT+X71Q== +electron-to-chromium@^1.4.601: + version "1.4.614" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz#2fe789d61fa09cb875569f37c309d0c2701f91c0" + integrity sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.3" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +emojilib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.4.0.tgz#ac518a8bb0d5f76dda57289ccb2fdf9d39ae721e" + integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw== -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== -es6-error@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-prettier@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af" + integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.11.7" + +eslint-plugin-unused-imports@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738" + integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ== + +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.20.1: + version "9.20.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" + integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.11.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.20.0" + "@eslint/plugin-kit" "^0.2.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -fast-url-parser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: - punycode "^1.3.2" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: - to-regex-range "^5.0.1" + "@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" -find-cache-dir@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-sha256@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-sha256/-/fast-sha256-1.3.0.tgz#7916ba2054eeb255982608cccd0f6660c79b7ae6" + integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" + reusify "^1.0.4" -find-cache-dir@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" + bser "2.1.1" -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" + flat-cache "^4.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: - locate-path "^3.0.0" + to-regex-range "^5.0.1" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" @@ -1747,139 +1769,82 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -follow-redirects@^1.15.4: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== - -foreground-child@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" - integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^3.0.2" - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" + locate-path "^6.0.0" + path-exists "^4.0.0" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -fromentries@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" - integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + flatted "^3.2.9" + keyv "^4.5.4" -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.1, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -glob-parent@~5.1.0, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + is-glob "^4.0.3" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1891,30 +1856,36 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -graceful-fs@^4.1.15: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -hamming-distance@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hamming-distance/-/hamming-distance-1.0.0.tgz#39bfa46c61f39e87421e4035a1be4f725dd7b931" - integrity sha512-hYz2IIKtyuZGfOqCs7skNiFEATf+v9IUNSOaQSr6Ll4JOxxWhOvXvc3mIdCW82Z3xW+zUoto7N/ssD4bDxAWoA== +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== has-flag@^3.0.0: version "3.0.0" @@ -1926,80 +1897,62 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hasha@^5.0.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" - integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - is-stream "^2.0.0" - type-fest "^0.8.0" + function-bind "^1.1.2" -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" +highlight.js@^10.7.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-proxy-agent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" + safer-buffer ">= 2.1.2 < 3.0.0" -https-proxy-agent@^5.0.0: +ignore-walk@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" + integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== dependencies: - agent-base "6" - debug "4" + minimatch "^5.0.1" -ignore-walk@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" - integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: - minimatch "^3.0.4" + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" @@ -2019,219 +1972,94 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== - dependencies: - has "^1.0.3" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has-tostringtag "^1.0.0" + hasown "^2.0.0" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.1, is-glob@~4.0.1: +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-hook@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" - integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== - dependencies: - append-transform "^2.0.0" - -istanbul-lib-instrument@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: - "@babel/core" "^7.7.5" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" + istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-processinfo@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" - integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== dependencies: - archy "^1.0.0" - cross-spawn "^7.0.3" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" istanbul-lib-coverage "^3.2.0" - p-map "^3.0.0" - rimraf "^3.0.0" - uuid "^8.3.2" + semver "^7.5.4" istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -2243,20 +2071,378 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" - integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== +istanbul-reports@^3.1.3: + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.4.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.13.1: +js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2264,10 +2450,10 @@ js-yaml@3.14.1, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -2276,38 +2462,65 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -just-extend@^4.0.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" - integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== +jsonc-parser@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" + integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" + json-buffer "3.0.1" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== locate-path@^5.0.0: version "5.0.0" @@ -2321,244 +2534,210 @@ locate-path@^6.0.0: resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: - p-locate "^5.0.0" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== + p-locate "^5.0.0" -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash@^4.17.15, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" +lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== -loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: - get-func-name "^2.0.0" + yallist "^3.0.2" -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: - pify "^4.0.1" - semver "^5.6.0" + yallist "^4.0.0" -make-dir@^3.0.0, make-dir@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +marked-terminal@^7.1.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-7.2.1.tgz#9c1ae073a245a03c6a13e3eeac6f586f29856068" + integrity sha512-rQ1MoMFXZICWNsKMiiHwP/Z+92PLKskTPXj+e7uwXmuMPkNn7iTqC+IvDekVm1MPeC9wYQeLxeFaOvudRR/XbQ== + dependencies: + ansi-escapes "^7.0.0" + ansi-regex "^6.1.0" + chalk "^5.3.0" + cli-highlight "^2.1.11" + cli-table3 "^0.6.5" + node-emoji "^2.1.3" + supports-hyperlinks "^3.1.0" + +marked@^9.1.2: + version "9.1.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-9.1.6.tgz#5d2a3f8180abfbc5d62e3258a38a1c19c0381695" + integrity sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - mime-db "1.52.0" + braces "^3.0.3" + picomatch "^2.3.1" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.1: +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -mocha@^8.1.1: - version "8.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" - he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mri@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== - -nise@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" - integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" - -nock@^13.2.7: - version "13.2.7" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.7.tgz#c93933b61df42f4f4b3a07fde946a4e209c0c168" - integrity sha512-R6NUw7RIPtKwgK7jskuKoEi4VFMqIHtV2Uu9K/Uegc4TA5cqe+oNMYslZcUmnVNQCTG6wcSqUBaGTDd7sq5srg== +mz@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.21" - propagate "^2.0.0" + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" -node-environment-flags@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" - integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +node-emoji@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-2.1.3.tgz#93cfabb5cc7c3653aa52f29d6ffb7927d8047c06" + integrity sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA== dependencies: - whatwg-url "^5.0.0" + "@sindresorhus/is" "^4.6.0" + char-regex "^1.0.2" + emojilib "^2.4.0" + skin-tone "^2.0.0" -node-preload@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" - integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== - dependencies: - process-on-spawn "^1.0.0" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -nyc@^15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" - integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== +npm-bundled@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" + integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== dependencies: - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - caching-transform "^4.0.0" - convert-source-map "^1.7.0" - decamelize "^1.2.0" - find-cache-dir "^3.2.0" - find-up "^4.1.0" - foreground-child "^2.0.0" - get-package-type "^0.1.0" - glob "^7.1.6" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-hook "^3.0.0" - istanbul-lib-instrument "^4.0.0" - istanbul-lib-processinfo "^2.0.2" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - make-dir "^3.0.0" - node-preload "^0.2.1" - p-map "^3.0.0" - process-on-spawn "^1.0.0" - resolve-from "^5.0.0" - rimraf "^3.0.0" - signal-exit "^3.0.2" - spawn-wrap "^2.0.0" - test-exclude "^6.0.0" - yargs "^15.0.2" + npm-normalize-package-bin "^2.0.0" -object-inspect@^1.12.0, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +npm-normalize-package-bin@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" + integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +npm-packlist@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" + integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" + glob "^8.0.1" + ignore-walk "^5.0.1" + npm-bundled "^2.0.0" + npm-normalize-package-bin "^2.0.0" -object.getownpropertydescriptors@^2.0.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: - array.prototype.reduce "^1.0.4" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" + path-key "^3.0.0" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== once@^1.3.0: version "1.4.0" @@ -2567,27 +2746,46 @@ once@^1.3.0: dependencies: wrappy "1" -p-limit@^2.0.0, p-limit@^2.2.0: +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-all@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb" + integrity sha512-qUZbvbBFVXm6uJ7U/WDiO0fv6waBMbjlCm4E66oZdRR+egswICarIdHyVSZZHudH8T5SF8x/JG0q0duFzPnlBw== + dependencies: + p-map "^4.0.0" + +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -2602,10 +2800,10 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" @@ -2614,25 +2812,39 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-hash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" - integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: - graceful-fs "^4.1.15" - hasha "^5.0.0" - lodash.flattendeep "^4.4.0" - release-zalgo "^1.0.0" + callsites "^3.0.0" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== +parse5-htmlparser2-tree-adapter@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== path-exists@^4.0.0: version "4.0.0" @@ -2644,7 +2856,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -2654,240 +2866,191 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pirates@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.1.0: +pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" -process-on-spawn@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" - integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== - dependencies: - fromentries "^1.2.0" - -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: - safe-buffer "^5.1.0" + fast-diff "^1.1.2" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" +prettier@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" + integrity sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw== -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: - picomatch "^2.2.1" + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + kleur "^3.0.3" + sisteransi "^1.0.5" -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== +publint@^0.2.12: + version "0.2.12" + resolved "https://registry.yarnpkg.com/publint/-/publint-0.2.12.tgz#d25cd6bd243d5bdd640344ecdddb3eeafdcc4059" + integrity sha512-YNeUtCVeM4j9nDiTT2OPczmlyzOkIXNtdDZnSuajAxS/nZ6j3t7Vs9SUB4euQNddiltIwu7Tdd3s+hr08fAsMw== dependencies: - "@babel/runtime" "^7.8.4" + npm-packlist "^5.1.3" + picocolors "^1.1.1" + sade "^1.8.1" -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" +pure-rand@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== - dependencies: - jsesc "~0.5.0" +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -release-zalgo@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" - integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: - es6-error "^4.0.1" + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.14.2: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -rimraf@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: - glob "^7.1.3" + queue-microtask "^1.2.2" -rxjs@^6.6.3: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== +sade@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" + integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== dependencies: - tslib "^1.9.0" + mri "^1.1.0" -safe-buffer@^5.1.0: +safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^5.6.0, semver@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: - randombytes "^2.1.0" + lru-cache "^6.0.0" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" +semver@^7.6.0: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== shebang-command@^2.0.0: version "2.0.0" @@ -2901,41 +3064,32 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.2: +signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -sinon@^9.2.0: - version "9.2.4" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" - integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== - dependencies: - "@sinonjs/commons" "^1.8.1" - "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/samsam" "^5.3.1" - diff "^4.0.2" - nise "^4.0.4" - supports-color "^7.1.0" +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^2.0.0: +skin-tone@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + resolved "https://registry.yarnpkg.com/skin-tone/-/skin-tone-2.0.0.tgz#4e3933ab45c0d4f4f781745d64b9f4c208e41237" + integrity sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA== + dependencies: + unicode-emoji-modifier-base "^1.0.0" -source-map-support@^0.5.16: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -2945,44 +3099,42 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg== - -spawn-wrap@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" - integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== - dependencies: - foreground-child "^2.0.0" - is-windows "^1.0.2" - make-dir "^3.0.0" - rimraf "^3.0.0" - signal-exit "^3.0.2" - which "^2.0.1" - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -stream-events@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" - integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: - stubs "^3.0.0" + escape-string-regexp "^2.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +standardwebhooks@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/standardwebhooks/-/standardwebhooks-1.0.0.tgz#5faa23ceacbf9accd344361101d9e3033b64324f" + integrity sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg== + dependencies: + "@stablelib/base64" "^1.0.0" + fast-sha256 "^1.3.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-to-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/string-to-stream/-/string-to-stream-3.0.1.tgz#480e6fb4d5476d31cb2221f75307a5dcb6638a42" + integrity sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg== + dependencies: + readable-stream "^3.4.0" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2991,30 +3143,12 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: - ansi-regex "^3.0.0" + safe-buffer "~5.2.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" @@ -3023,27 +3157,30 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-json-comments@3.1.1: +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -stubs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" - integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== - -supports-color@8.1.1, supports-color@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" +superstruct@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" + integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ== supports-color@^5.3.0: version "5.5.0" @@ -3052,28 +3189,39 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz#b56150ff0173baacc15f21956450b61f2b18d3ac" + integrity sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -teeny-request@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.1.tgz#2b0d156f4a8ad81de44303302ba8d7f1f05e20e6" - integrity sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg== +synckit@^0.11.7: + version "0.11.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457" + integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A== dependencies: - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - stream-events "^1.0.5" - uuid "^8.0.0" + "@pkgr/core" "^0.2.4" test-exclude@^6.0.0: version "6.0.0" @@ -3084,6 +3232,25 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -3096,165 +3263,177 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +ts-api-utils@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd" + integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w== + +ts-jest@^29.1.0: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + +ts-node@^10.5.0: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" + yn "3.1.1" + +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": + version "1.1.9" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" + dependencies: + debug "^4.3.7" + fast-glob "^3.3.2" + get-stdin "^8.0.0" + p-all "^3.0.0" + picocolors "^1.1.1" + signal-exit "^3.0.7" + string-to-stream "^3.0.1" + superstruct "^1.0.4" + tslib "^2.8.1" + yargs "^17.7.2" + +tsconfig-paths@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" -tslib@^1.9.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: +type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.8.0: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typescript@^4.3.2: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +typescript-eslint@8.31.1: + version "8.31.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.31.1.tgz#b77ab1e48ced2daab9225ff94bab54391a4af69b" + integrity sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA== + dependencies: + "@typescript-eslint/eslint-plugin" "8.31.1" + "@typescript-eslint/parser" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + +typescript@5.6.1-rc: + version "5.6.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.1-rc.tgz#d5e4d7d8170174fed607b74cc32aba3d77018e02" + integrity sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ== + +typescript@5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +unicode-emoji-modifier-base@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz#dbbd5b54ba30f287e2a8d5a249da6c0cef369459" + integrity sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g== -update-browserslist-db@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" - integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" -urlgrey@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-1.0.0.tgz#72d2f904482d0b602e3c7fa599343d699bbe1017" - integrity sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: - fast-url-parser "^1.1.3" - -uuid@^8.0.0, uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + punycode "^2.1.0" -v8flags@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== +v8-to-istanbul@^9.0.1: + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" -which@2.0.2, which@^2.0.1: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -3269,55 +3448,40 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + signal-exit "^3.0.7" y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@16.2.0, yargs@^16.2.0: +yargs@^16.0.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -3330,22 +3494,23 @@ yargs@16.2.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^15.0.2: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== +yargs@^17.3.1, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0"