diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ed723b3..0000000 --- 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 0000000..43fd5a7 --- /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 0000000..3f5e57c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,88 @@ +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: '20' + + - 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: '20' + + - 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 + 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: '20' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index 7263c90..0000000 --- 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 e1b73f8..0000000 --- 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 0000000..678e029 --- /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 919ac52..d98d51a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,10 @@ +.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/ + diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index fe5128b..0000000 --- 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 eaa22bd..0000000 --- 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 be4975e..0000000 --- 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 0000000..3548c5a --- /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 0000000..af75ada --- /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 0000000..e5fc86c --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "7.0.0-alpha.1" +} diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 0000000..b0086e9 --- /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-bc7c0d27962b30c19c778656988e154b54696819389289f34420a5e5fdfbd3b8.yml +openapi_spec_hash: 1bfde02a63416c036e9545927f727459 +config_hash: b415c06a3b29485af4601beb94ae1aeb diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..e4feee6 --- /dev/null +++ b/Brewfile @@ -0,0 +1 @@ +brew "node" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ec5268..553b87b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,76 @@ -## Changelog +# Changelog -### SDK Version 6.0.0 +## 7.0.0-alpha.1 (2025-09-06) -### Breaking changes +Full Changelog: [v0.0.1-alpha.0...v7.0.0-alpha.1](https://github.com/imagekit-developer/imagekit-nodejs/compare/v0.0.1-alpha.0...v7.0.0-alpha.1) -**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 url signing and test cases ([b1594d8](https://github.com/imagekit-developer/imagekit-nodejs/commit/b1594d8e0e416811bb7a87e3d14492725dc1b2d4)) +* **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 ([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)) +* **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)) +* **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)) +* **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)) +* **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)) +* 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)) +* **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 + +* **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 0000000..b792052 --- /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 0000000..e7a4d16 --- /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 37a4832..0000000 --- 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 dbbf7a5..5f46d23 100644 --- a/README.md +++ b/README.md @@ -1,1293 +1,602 @@ -[ImageKit.io](https://imagekit.io) +# Image Kit TypeScript API Library -# ImageKit.io Node.js SDK +[![NPM version]()](https://npmjs.org/package/@imagekit/nodejs) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@imagekit/nodejs) -[![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) +This library provides convenient access to the Image Kit REST API from server-side TypeScript or JavaScript. -Node.js SDK for [ImageKit](https://imagekit.io/) implements the new APIs and interface for different file operations. +The REST API documentation can be found on [imagekit.io](https://imagekit.io/docs). The full API of this library can be found in [api.md](api.md). -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) +It is generated with [Stainless](https://www.stainless.com/). ## Installation -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. - -``` -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 +```sh +npm install git+ssh://git@github.com:imagekit-developer/imagekit-nodejs.git ``` -## Initialization - -```js -import ImageKit from "imagekit"; - -// or - -var ImageKit = require("imagekit"); - -var imagekit = new ImageKit({ - publicKey : "your_public_api_key", - privateKey : "your_private_api_key", - urlEndpoint : "https://ik.imagekit.io/your_imagekit_id/" -}); -``` +> [!NOTE] +> Once this package is [published to npm](https://www.stainless.com/docs/guides/publish), this will become: `npm install @imagekit/nodejs` ## 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 - -**1. Using image path and image hostname or endpoint** - -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. - -```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. - - -```js -var imageURL = imagekit.url({ - src : "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation : [{ - "height" : "300", - "width" : "400" - }] -}); -``` - -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. | +The full API of this library can be found in [api.md](api.md). -#### 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" -}); -``` -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400%3Art-90 -``` +import ImageKit from '@imagekit/nodejs'; -**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" - }] +const client = new ImageKit({ + privateAPIKey: process.env['IMAGEKIT_PRIVATE_API_KEY'], // This is the default and can be omitted + password: process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'], // This is the default and can be omitted }); -``` -``` -//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 +const response = await client.files.upload({ + file: fs.createReadStream('path/to/file'), + fileName: 'file-name.jpg', }); -``` -``` -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. - -**Text as overlays** - -You can add any text string over a base video or image using a text layer (l-text). - -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" - }] -}); -``` -**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 -``` - -**Solid color blocks as overlays** - -You can add solid color blocks 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/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 +console.log(response.videoCodec); ``` -**5. Arithmetic expressions in transformations** +### Request & Response types -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. +This library includes TypeScript definitions for all request params and response fields. You may import and use them like so: -For example: + +```ts +import ImageKit from '@imagekit/nodejs'; -```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" - }] +const client = new ImageKit({ + privateAPIKey: process.env['IMAGEKIT_PRIVATE_API_KEY'], // This is the default and can be omitted + password: process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'], // This is the default and can be omitted }); -``` -**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 +const params: ImageKit.FileUploadParams = { + file: fs.createReadStream('path/to/file'), + fileName: 'file-name.jpg', +}; +const response: ImageKit.FileUploadResponse = await client.files.upload(params); ``` +Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors. -#### 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); -}); -``` +## File uploads -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. +Request parameters that correspond to file uploads can be passed in many different forms: +- `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 +```ts +import fs from 'fs'; +import ImageKit, { toFile } from '@imagekit/nodejs'; -## File Management +const client = new ImageKit(); -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. +// If you have access to Node `fs` we recommend using `fs.createReadStream()`: +await client.files.upload({ file: fs.createReadStream('/path/to/file'), fileName: 'fileName' }); -**List & Search Files** +// 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' }); -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. +// You can also pass a `fetch` `Response`: +await client.files.upload({ file: await fetch('https://somesite/file'), fileName: 'fileName' }); -```js -// Using Callback Function - -imagekit.listFiles({ - skip : 10, - limit : 10 -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - - -// Using Promises - -imagekit.listFiles({ - skip : 10, - limit : 10 -}).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' }); ``` -**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). +## URL generation -```js -// Using Callback Function +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. -imagekit.getFileDetails("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +### Basic URL generation +Generate a simple URL without any transformations: -// Using Promises +```ts +import ImageKit from '@imagekit/nodejs'; -imagekit.getFileDetails("file_id") -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); +const client = new ImageKit({ + privateAPIKey: process.env['IMAGEKIT_PRIVATE_API_KEY'], + password: process.env['ORG_MY_PASSWORD_TOKEN'], }); -``` - -**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); -}); - -// Using Promises - -imagekit.getFileVersions("file_id") -}).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 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 transformations +Apply common transformations like resizing, cropping, and format conversion: -// Using Promises - -imagekit.getFileVersionDetails({ - fileId: "file_id", - versionId: "version_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 ``` -**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. +### URL generation with image overlay -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`. +Add image overlays to your base image: -```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); -}); - - -// Using Promises - -imagekit.updateFileDetails("file_id", { - publish: { - isPublished: true, - includeFileVersions: true - } -}).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 ``` -**Bulk Add tags** +### URL generation with text overlay -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. +Add customized text overlays: -```js -// Using Callback Function - -imagekit.bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"], function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - -// Using Promises - -imagekit.bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"]).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 ``` -**Bulk Remove tags** - -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. +### URL generation with multiple overlays -```js -// Using Callback Function +Combine multiple overlays for complex compositions: -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); +```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 Remove AI Tags** +### Signed URLs for secure delivery -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. - -```js -// Using Callback Function +Generate signed URLs that expire after a specified time for secure content delivery: -imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-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.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-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) ``` -**Delete File** +### Authentication parameters for client-side uploads -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. +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.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); -}); +// 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' } ``` -**Delete File Version** +These authentication parameters can be used in client-side upload forms to securely upload files without exposing your private API key. -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). - -```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 +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | -imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"], function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +### Retries +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. -// Using Promises - -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); -}); - -// Using Promises +## Advanced Usage -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); -}); -``` +### Accessing raw Response data (e.g., headers) -**Restore File Version** +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 the file version as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/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. -```js -// Using Callback Function - -imagekit.restoreFileVersion({ - fileId: "file_id", - versionId: "version_id" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); + +```ts +const client = new ImageKit(); -// Using Promises +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 -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** - -This will create a new folder as per [API documentation here](https://docs.imagekit.io/api-reference/media-api/create-folder). +### Logging -```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 +> [!IMPORTANT] +> All log messages are intended for debugging only. The format and content of log messages +> may change between releases. -imagekit.createFolder({ - folderName: "new_folder", - parentFolderPath: "source/folder/path" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); -``` +#### Log levels -**Delete Folder** +The log level can be configured in two ways: -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). - -```js -// Using Callback Function - -imagekit.deleteFolder("folderPath", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +1. Via the `IMAGE_KIT_LOG` environment variable +2. Using the `logLevel` client option (overrides the environment variable if set) -// Using Promises +```ts +import ImageKit from '@imagekit/nodejs'; -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); -}); - -// Using Promises +const logger = pino(); -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** +### Making custom/undocumented requests -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. +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. -```js -// Using Callback Function +#### Undocumented endpoints -imagekit.getBulkJobStatus("jobId", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +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. -// Using Promises - -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** - -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. +#### Undocumented request params -```js -// Using Callback Function +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. -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); -}); -``` - -**Get File Metadata** - -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). +If you want to use a different `fetch` function, you can either polyfill the global: -```js -// Using Callback Function -imagekit.getFileMetadata("file_id", function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +```ts +import fetch from 'my-fetch'; - -// 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. - -```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); -}); +Or pass it to the client: +```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** - -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) - -```js -// Using Callback Function - -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); - } -); +### Fetch options +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.) -// Using Promises +```ts +import ImageKit from '@imagekit/nodejs'; -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) +To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy +options to requests: -```js -// Using Callback Function - -imagekit.getCustomMetadataFields( - { - includeDeleted: false // optional - }, - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); + **Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)] +```ts +import ImageKit from '@imagekit/nodejs'; +import * as undici from 'undici'; -// Using Promises - -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) - -```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); - } -); +```ts +import ImageKit from '@imagekit/nodejs'; - -// 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** - -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); - } -); - + **Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)] -// Using Promises +```ts +import ImageKit from 'npm:@imagekit/nodejs'; -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" -} -``` +## Frequently Asked Questions -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. +## Semantic versioning -### Distance calculation between two pHash values +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: -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. +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. -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. +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -```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 +We are keen for your feedback; please open an [issue](https://www.github.com/imagekit-developer/imagekit-nodejs/issues) with questions, bugs, or suggestions. -```js -imagekit.pHashDistance('f06830ca9f1e3e90', 'f06830ca9f1e3e90'); -// output: 0 (same image) - -imagekit.pHashDistance('2d5ad3936d2e015b', '2d6ed293db36a4fb'); -// output: 17 (similar images) +## Requirements -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. - -```javascript -// Success -var response = await imagekit.getPurgeCacheStatus(requestId); -console.log(response.$ResponseMetadata.statusCode); // 200 - -// {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} -console.log(response.$ResponseMetadata.headers); - -// 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 0000000..8e64327 --- /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 0000000..78b8a89 --- /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 +- UpdateFileDetailsRequest +- 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 a24dfd2..0000000 --- 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 0000000..6b43775 --- /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 0000000..45e8aa8 --- /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 0000000..68d1b0b --- /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/**'], + rules: { + 'no-restricted-imports': 'off', + }, + }, +); diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 0000000..0651c89 --- /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 59d984f..0000000 --- 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 0000000..f7631d7 --- /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 ba10e6c..0000000 --- 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 fb0e649..0000000 --- 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 9329db3..0000000 --- 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 eef7cab..0000000 --- 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 c29d1a0..0000000 --- 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 6f7c002..0000000 --- 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 7efcee0..0000000 --- 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 358ac53..0000000 --- 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 aaa9bc9..0000000 --- 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 642310f..0000000 --- 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 f6b6cb7..0000000 --- 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 b2c489e..0000000 --- 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 b33ae42..0000000 --- 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 a53ca4f..0000000 --- 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 122a4de..0000000 --- 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 bba9321..0000000 --- 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 1a26a16..0000000 --- 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 2361a6e..0000000 --- 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 c7b0e38..0000000 --- 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 98b338d..0000000 --- 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 a2990f6..0000000 --- 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 47389b2..0000000 --- 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 9ca4437..0000000 --- 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 d3dc6ac..0000000 --- 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 538a897..0000000 --- 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 194afdc..0000000 --- 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 a3bb4ef..0000000 --- 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 752ab08..0000000 --- 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 5ee18da..0000000 --- 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 72f4ac3..0000000 --- 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 8d17ace..0000000 --- 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 7d1a857..0000000 --- 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 8731997..0000000 --- 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 35cd2d5..0000000 --- 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 40f220e..0b3aab4 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,71 @@ { - "name": "imagekit", - "version": "6.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" + "name": "@imagekit/nodejs", + "version": "7.0.0-alpha.1", + "description": "The official TypeScript library for the Image Kit API", + "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/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..1ebd0bd --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,64 @@ +{ + "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"] +} diff --git a/sample/README.md b/sample/README.md deleted file mode 100644 index bb0fdc2..0000000 --- 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 032f2d9..0000000 --- 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 fb22c51..0000000 --- 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 8102e27..0000000 Binary files a/sample/test_image.jpg and /dev/null differ diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000..062a034 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then + brew bundle check >/dev/null 2>&1 || { + echo "==> Installing Homebrew dependencies…" + brew bundle + } +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 0000000..470e580 --- /dev/null +++ b/scripts/build @@ -0,0 +1,51 @@ +#!/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 diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000..7a75640 --- /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 0000000..3ffb78a --- /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 0000000..0b28f6e --- /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/test b/scripts/test new file mode 100755 index 0000000..7bce051 --- /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 0000000..b3477c0 --- /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 0000000..1354eb4 --- /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 0000000..86c56df --- /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 0000000..e5e10b3 --- /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 0000000..79d1888 --- /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 0000000..7c24f56 --- /dev/null +++ b/scripts/utils/make-dist-package-json.cjs @@ -0,0 +1,21 @@ +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\//, './'); +} + +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 0000000..deae575 --- /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 0000000..0870aeb --- /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 0000000..8c775ee --- /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 0000000..6a110c3 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,930 @@ +// 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, + UpdateFileDetailsRequest, +} 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 (it starts with `private_`). + * You can view and manage API keys in the [dashboard](https://imagekit.io/dashboard/developer/api-keys). + * + */ + privateAPIKey?: string | undefined; + + /** + * ImageKit Basic Auth only uses the username field and ignores the password. + * This field is unused. + * + */ + password?: string | null | undefined; + + /** + * Your ImageKit webhook secret. This is used by the SDK to verify webhook signatures. It starts with a `whsec_` prefix. + * You can view and manage your webhook secret in the [dashboard](https://imagekit.io/dashboard/developer/webhooks). + * Treat the secret like a password, keep it private and do not expose it publicly. + * Learn more about [webhook verification](https://imagekit.io/docs/webhooks#verify-webhook-signature). + * + */ + 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 { + privateAPIKey: 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.privateAPIKey=process.env['IMAGEKIT_PRIVATE_API_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'), + privateAPIKey = readEnv('IMAGEKIT_PRIVATE_API_KEY'), + password = readEnv('OPTIONAL_IMAGEKIT_IGNORES_THIS') ?? 'do_not_set', + webhookSecret = readEnv('IMAGEKIT_WEBHOOK_SECRET') ?? null, + ...opts + }: ClientOptions = {}) { + if (privateAPIKey === undefined) { + throw new Errors.ImageKitError( + "The IMAGEKIT_PRIVATE_API_KEY environment variable is missing or empty; either provide it, or instantiate the ImageKit client with an privateAPIKey option, like new ImageKit({ privateAPIKey: 'My Private API Key' }).", + ); + } + + const options: ClientOptions = { + privateAPIKey, + 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.privateAPIKey = privateAPIKey; + 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, + privateAPIKey: this.privateAPIKey, + 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.privateAPIKey && this.password && values.get('authorization')) { + return; + } + if (nulls.has('authorization')) { + return; + } + + throw new Error( + 'Could not resolve authentication method. Expected the privateAPIKey or password to be set. Or for the "Authorization" headers to be explicitly omitted', + ); + } + + protected async authHeaders(opts: FinalRequestOptions): Promise { + if (!this.privateAPIKey) { + return undefined; + } + + if (!this.password) { + return undefined; + } + + const credentials = `${this.privateAPIKey}:${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 UpdateFileDetailsRequest as UpdateFileDetailsRequest, + 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 0000000..485fce8 --- /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 0000000..53821f1 --- /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 0000000..47d1cc2 --- /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 0000000..5152510 --- /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 0000000..2882ca6 --- /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 0000000..fc55f46 --- /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 0000000..71734d8 --- /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 0000000..3ef5a25 --- /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 0000000..c23d3bd --- /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 0000000..e82d95c --- /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 0000000..82c7b14 --- /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 0000000..c724a9d --- /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 0000000..10d1ca9 --- /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 0000000..2aabf9a --- /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 0000000..8ddf7b0 --- /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 0000000..e4dc710 --- /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 0000000..245e849 --- /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 0000000..b668dfc --- /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 0000000..bfc86b4 --- /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 0000000..3cbfacc --- /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 0000000..2312df3 --- /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 0000000..8da627a --- /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 0000000..2d84800 --- /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 0000000..7da4129 --- /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 0000000..dbf0664 --- /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 0000000..65e5296 --- /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 0000000..b0e53aa --- /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 0000000..532f2c3 --- /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 === undefined) { + return undefined; + } + return coerceInteger(value); +}; + +export const maybeCoerceFloat = (value: unknown): number | undefined => { + if (value === undefined) { + return undefined; + } + return coerceFloat(value); +}; + +export const maybeCoerceBoolean = (value: unknown): boolean | undefined => { + if (value === undefined) { + 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 0000000..7554f8b --- /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 0000000..174b4b8 --- /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 0000000..4e94ada --- /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 0000000..17fdf74 --- /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 0000000..363e351 --- /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 0000000..b283d57 --- /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 0000000..b9db299 --- /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 0000000..82d1b23 --- /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 0000000..34a0246 --- /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 0000000..f59a468 --- /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 0000000..7bd424c --- /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 0000000..87e703a --- /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 0000000..19a35ad --- /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 0000000..1542e94 --- /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 0000000..f8fa04c --- /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 0000000..2b3a43c --- /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 0000000..ca56a44 --- /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 0000000..60cc172 --- /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; + + /** + * 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 0000000..bffa32e --- /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 0000000..3a04792 --- /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 0000000..c011d11 --- /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 0000000..b959081 --- /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 0000000..3c9f4fc --- /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 0000000..d06b270 --- /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 0000000..10a917e --- /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 0000000..46a5299 --- /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 0000000..35dec26 --- /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 0000000..a2007b6 --- /dev/null +++ b/src/resources/files/files.ts @@ -0,0 +1,1452 @@ +// 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', { + * customCoordinates: '10,10,100,100', + * customMetadata: { brand: 'Nike', color: 'red' }, + * extensions: [ + * { name: 'remove-bg', options: { add_shadow: true } }, + * { + * name: 'google-auto-tagging', + * minConfidence: 80, + * maxTags: 10, + * }, + * { + * name: 'aws-auto-tagging', + * minConfidence: 80, + * maxTags: 10, + * }, + * { name: 'ai-auto-description' }, + * ], + * removeAITags: ['car', 'vehicle', 'motorsports'], + * tags: ['tag1', 'tag2'], + * webhookUrl: + * 'https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a', + * }); + * ``` + */ + update( + fileID: string, + body: FileUpdateParams | null | undefined = {}, + 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; + } + } +} + +export type UpdateFileDetailsRequest = + | UpdateFileDetailsRequest.UpdateFileDetails + | UpdateFileDetailsRequest.ChangePublicationStatus; + +export namespace UpdateFileDetailsRequest { + 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; + + /** + * 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 UpdateFileDetailsRequest as UpdateFileDetailsRequest, + 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 0000000..0082557 --- /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 UpdateFileDetailsRequest, + 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 0000000..1841640 --- /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 0000000..4cd160b --- /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 0000000..7aaeee3 --- /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 0000000..916e304 --- /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 0000000..7759a7d --- /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 0000000..a9fc92b --- /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 0000000..4d48568 --- /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.privateAPIKey, + 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.privateAPIKey) { + 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.privateAPIKey); + } +} + +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 0000000..de6db99 --- /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 UpdateFileDetailsRequest, + 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 0000000..6d73d69 --- /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 0000000..a95d745 --- /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 0000000..b2ef647 --- /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 0000000..653ebcd --- /dev/null +++ b/src/version.ts @@ -0,0 +1 @@ +export const VERSION = '7.0.0-alpha.1'; // x-release-please-version diff --git a/test-e2e.sh b/test-e2e.sh deleted file mode 100644 index e9aa713..0000000 --- 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 0000000..4aee2b5 --- /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({ + privateAPIKey: 'My Private API 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 0000000..4f09652 --- /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({ + privateAPIKey: 'My Private API 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 0000000..5e83c3c --- /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({ + privateAPIKey: 'My Private API 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 0000000..a67f9a3 --- /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({ + privateAPIKey: 'My Private API 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 0000000..1012110 --- /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({ + privateAPIKey: 'My Private API 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 0000000..0354e08 --- /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({ + privateAPIKey: 'My Private API 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 0000000..9ebf4d3 --- /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({ + privateAPIKey: 'My Private API 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 0000000..823b35b --- /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({ + privateAPIKey: 'My Private API 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 0000000..a11ad62 --- /dev/null +++ b/tests/api-resources/files/files.test.ts @@ -0,0 +1,212 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import ImageKit, { toFile } from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateAPIKey: 'My Private API 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('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.files.update( + 'fileId', + { + customCoordinates: 'customCoordinates', + customMetadata: { foo: 'bar' }, + description: 'description', + 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' }, + ], + removeAITags: ['string'], + tags: ['tag1', 'tag2'], + webhookUrl: 'https://example.com', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(ImageKit.NotFoundError); + }); + + // 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 0000000..8b2c74c --- /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({ + privateAPIKey: 'My Private API 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 0000000..24800f8 --- /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({ + privateAPIKey: 'My Private API 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 0000000..e42b641 --- /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({ + privateAPIKey: 'My Private API 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 0000000..3e69820 --- /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({ + privateAPIKey: 'My Private API 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 0000000..0d223f4 --- /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({ + privateAPIKey: 'My Private API 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 0000000..5191005 --- /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 0000000..818d0b8 --- /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 f1f7b2c..0000000 --- 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 18ad8ca..0000000 --- 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/data/index.js b/tests/data/index.js deleted file mode 100644 index 6d7d2f5..0000000 --- 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 8102e27..0000000 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 0e411d9..0000000 --- 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 e46bf8b..0000000 --- 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 75dcaea..0000000 --- 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 0000000..c978df1 --- /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/helper-authentication.test.ts b/tests/helper-authentication.test.ts new file mode 100644 index 0000000..2b40e31 --- /dev/null +++ b/tests/helper-authentication.test.ts @@ -0,0 +1,62 @@ +import ImageKit from '@imagekit/nodejs'; + +const client = new ImageKit({ + privateAPIKey: 'private_key_test', + password: 'My Password', + 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/helpers/errors.js b/tests/helpers/errors.js deleted file mode 100644 index 464df30..0000000 --- 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 1dbbc24..0000000 --- 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 0000000..d3799f7 --- /dev/null +++ b/tests/index.test.ts @@ -0,0 +1,826 @@ +// 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' }, + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API Key', + password: 'My Password', + }); + + await forceAPIResponseForClient(client); + expect(debugMock).toHaveBeenCalled(); + }); + + test('default logLevel is warn', async () => { + const client = new ImageKit({ privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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, + privateAPIKey: 'My Private API 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, + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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' }, + privateAPIKey: 'My Private API 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' }, + privateAPIKey: 'My Private API 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' }, + privateAPIKey: 'My Private API 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/', + privateAPIKey: 'My Private API 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/', + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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/', + privateAPIKey: 'My Private API 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/', + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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', + privateAPIKey: 'My Private API 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({ privateAPIKey: 'My Private API 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({ privateAPIKey: 'My Private API 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({ privateAPIKey: 'My Private API Key', password: 'My Password' }); + expect(client.baseURL).toEqual('https://api.imagekit.io'); + }); + + test('in request options', () => { + const client = new ImageKit({ privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ privateAPIKey: 'My Private API 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, + privateAPIKey: 'My Private API Key', + password: 'My Password', + }); + expect(client.maxRetries).toEqual(4); + + // default + const client2 = new ImageKit({ privateAPIKey: 'My Private API 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, + privateAPIKey: 'My Private API 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' }, + privateAPIKey: 'My Private API 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, + privateAPIKey: 'My Private API 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_API_KEY'] = 'My Private API Key'; + process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'] = 'My Password'; + const client = new ImageKit(); + expect(client.privateAPIKey).toBe('My Private API Key'); + expect(client.password).toBe('My Password'); + }); + + test('with overridden environment variable arguments', () => { + // set options via env var + process.env['IMAGEKIT_PRIVATE_API_KEY'] = 'another My Private API Key'; + process.env['OPTIONAL_IMAGEKIT_IGNORES_THIS'] = 'another My Password'; + const client = new ImageKit({ privateAPIKey: 'My Private API Key', password: 'My Password' }); + expect(client.privateAPIKey).toBe('My Private API Key'); + expect(client.password).toBe('My Password'); + }); +}); + +describe('request building', () => { + const client = new ImageKit({ privateAPIKey: 'My Private API 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({ privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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({ + privateAPIKey: 'My Private API 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 883753e..0000000 --- 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 6f9defa..0000000 --- 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 0000000..7a5a43c --- /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 5699a3a..0000000 --- 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 6ca3079..0000000 --- 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 0000000..4f2ca2e --- /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 0000000..c319315 --- /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 61be733..0000000 --- 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 935c6f1..0000000 --- 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 0000000..80ccae1 --- /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 689e234..0000000 --- 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/url-generation/basic.test.ts b/tests/url-generation/basic.test.ts new file mode 100644 index 0000000..f22a1a1 --- /dev/null +++ b/tests/url-generation/basic.test.ts @@ -0,0 +1,1337 @@ +import ImageKit from '@imagekit/nodejs'; +import type { SrcOptions } from '../../src/resources/shared'; + +const client = new ImageKit({ + privateAPIKey: 'My Private API Key', + password: 'My Password', + 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/url-generation/buildTransformationString.test.ts b/tests/url-generation/buildTransformationString.test.ts new file mode 100644 index 0000000..44310fc --- /dev/null +++ b/tests/url-generation/buildTransformationString.test.ts @@ -0,0 +1,32 @@ +import ImageKit from '@imagekit/nodejs'; +import type { Transformation } from '../../src/resources/shared'; + +const client = new ImageKit({ + privateAPIKey: 'My Private API Key', + password: 'My Password', + 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/url-generation/overlay.test.ts b/tests/url-generation/overlay.test.ts new file mode 100644 index 0000000..7d43bd7 --- /dev/null +++ b/tests/url-generation/overlay.test.ts @@ -0,0 +1,639 @@ +import ImageKit from '@imagekit/nodejs'; +import { safeBtoa } from '../../src/lib/transformation-utils'; + +const client = new ImageKit({ + privateAPIKey: 'My Private API Key', + password: 'My Password', + 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/url-generation/signing.test.ts b/tests/url-generation/signing.test.ts new file mode 100644 index 0000000..38afeef --- /dev/null +++ b/tests/url-generation/signing.test.ts @@ -0,0 +1,152 @@ +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({ + privateAPIKey: 'dummy-key', + password: 'My Password', + 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/webhook-signature.js b/tests/webhook-signature.js deleted file mode 100644 index 4e3dd4a..0000000 --- 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 0000000..384ddac --- /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 0000000..3d7881a --- /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 0000000..849e070 --- /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 0000000..c550e29 --- /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 69e4cea..0136ffb 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 231c167..0000000 --- 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 7fa5d2c..0000000 --- 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 dc2f3d8..0000000 --- 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 aa1bf85..0000000 --- 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 96c359e..0000000 --- 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 5b89e51..0000000 --- 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 96e2b15..0000000 --- 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 2402882..0000000 --- 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 f033c13..1935915 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"