diff --git a/.babelrc b/.babelrc deleted file mode 100644 index ed723b35..00000000 --- a/.babelrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "presets": [ - "@babel/preset-env", - "@babel/preset-typescript" - ], - "plugins" : [ - "babel-plugin-replace-ts-export-assignment" - ] -} \ No newline at end of file diff --git a/.fernignore b/.fernignore new file mode 100644 index 00000000..084a8ebb --- /dev/null +++ b/.fernignore @@ -0,0 +1 @@ +# Specify files that shouldn't be modified by Fern diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7f884eea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up node + uses: actions/setup-node@v3 + + - name: Compile + run: yarn && yarn build + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up node + uses: actions/setup-node@v3 + + - name: Compile + run: yarn && yarn test diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index 7263c90c..00000000 --- a/.github/workflows/nodejs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Node CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x, 14.x, 16.x, 18.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: Test and report coverage - run: | - npm i -g yarn - yarn install - yarn test - yarn test-e2e - # yarn report-coverage - env: - CI: true diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml deleted file mode 100644 index e1b73f85..00000000 --- a/.github/workflows/npmpublish.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Publish - -on: - release: - types: [published] - - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x, 14.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install, build, and test - run: | - npm i -g yarn - yarn install - yarn test - yarn test-e2e - env: - CI: true - - publish: - needs: build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 12 - registry-url: https://registry.npmjs.org/ - - name: yarn publish - run: | - npm i -g yarn - yarn config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN - yarn install - yarn publish - env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} - CI: true diff --git a/.gitignore b/.gitignore index 919ac523..72271e04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,3 @@ 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 +.DS_Store +/dist \ No newline at end of file diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index fe5128ba..00000000 --- a/.mocharc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extension": ["ts"], - "spec": "tests/*.js", - "require": "babel-register.js" -} \ No newline at end of file diff --git a/.npmignore b/.npmignore index eaa22bd7..383dd369 100644 --- a/.npmignore +++ b/.npmignore @@ -1,19 +1,10 @@ -.github +node_modules +src 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 +.gitignore +.github +.fernignore +.prettierrc.yml +tsconfig.json +yarn.lock +pnpm-lock.yaml \ No newline at end of file diff --git a/.nycrc b/.nycrc deleted file mode 100644 index be4975e7..00000000 --- a/.nycrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "check-coverage": true, - "lines": 95 -} \ No newline at end of file diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 00000000..0c06786b --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,2 @@ +tabWidth: 4 +printWidth: 120 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7ec52687..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -## Changelog - -### SDK Version 6.0.0 - -### Breaking changes - -**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. - -``` -const result = await imagekit.listFiles({ skip: 0, limit: 10 }); - -# 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 - } -}); -``` - - -### SDK Version 5.0.0 - -#### Breaking changes - -**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. - -**2. Remove Node.js 10.x support** -* In version 5.0.0, we've removed support for Node.js version 10.x. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 37a4832f..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -Released under the MIT license. \ No newline at end of file diff --git a/README.md b/README.md index dbbf7a59..7674b024 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,14 @@ -[ImageKit.io](https://imagekit.io) +# ImagekitDeveloper TypeScript Library -# ImageKit.io Node.js SDK +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fimagekit-developer%2Fimagekit-nodejs) +[![npm shield](https://img.shields.io/npm/v/imagekit)](https://www.npmjs.com/package/imagekit) -[![Node CI](https://github.com/imagekit-developer/imagekit-nodejs/workflows/Node%20CI/badge.svg)](https://github.com/imagekit-developer/imagekit-nodejs/) -[![npm version](https://img.shields.io/npm/v/imagekit)](https://www.npmjs.com/package/imagekit) -[![codecov](https://codecov.io/gh/imagekit-developer/imagekit-nodejs/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-nodejs) -[![Try imagekit on RunKit](https://badge.runkitcdn.com/imagekit.svg)](https://npm.runkit.com/imagekit) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Twitter Follow](https://img.shields.io/twitter/follow/imagekitio?label=Follow&style=social)](https://twitter.com/ImagekitIo) - -Node.js SDK for [ImageKit](https://imagekit.io/) implements the new APIs and interface for different file operations. - -ImageKit is complete media storage, optimization, and transformation solution that comes with an [image and video CDN](https://imagekit.io/features/imagekit-infrastructure). It can be integrated with your existing infrastructure - storage like AWS S3, web servers, your CDN, and custom domain names, allowing you to deliver optimized images in minutes with minimal code changes. - -##### Table of contents -* [Changelog](#changelog) -* [Installation](#installation) -* [Initialization](#initialization) -* [URL generation](#url-generation) -* [File upload](#file-upload) -* [File management](#file-management) -* [Utility functions](#utility-functions) -* [Rate limits](#rate-limits) -* [Support](#support) -* [Links](#links) +The ImagekitDeveloper TypeScript library provides convenient access to the ImagekitDeveloper API from TypeScript. ## 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 i -s imagekit ``` ## Initialization @@ -49,18 +21,27 @@ import ImageKit from "imagekit"; 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/" + publicKey: "your_public_api_key", + privateKey: "your_private_api_key", + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/", }); ``` ## 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` +Instantiate and use the client with the following: + +```typescript +import { createReadStream } from "fs"; +import { ImageKitClient } from "imagekit"; +import * as fs from "fs"; + +const client = new ImageKitClient({ username: "YOUR_USERNAME", password: "YOUR_PASSWORD" }); +await client.files.upload({ + file: fs.createReadStream("/path/to/your/file"), + fileName: "fileName", +}); +``` ## URL Generation @@ -71,12 +52,14 @@ This method allows you to create an URL to access a file using the relative file ```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" - }] + path: "/default-image.jpg", + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", + transformation: [ + { + height: "300", + width: "400", + }, + ], }); ``` @@ -90,14 +73,15 @@ https://ik.imagekit.io/your_imagekit_id/endpoint/tr:h-300,w-400/default-image.jp 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" - }] + src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", + transformation: [ + { + height: "300", + width: "400", + }, + ], }); ``` @@ -107,36 +91,40 @@ 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. | +| Option | Description | +| :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| urlEndpoint | Optional. The base URL to be appended before the path of the image. If not specified, the URL Endpoint specified at the time of SDK initialization is used. For example, https://ik.imagekit.io/your_imagekit_id/endpoint/ | +| path | Conditional. This is the path at which the image exists. For example, `/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | +| src | Conditional. This is the complete URL of an image already mapped to ImageKit. For example, `https://ik.imagekit.io/your_imagekit_id/endpoint/path/to/image.jpg`. Either the `path` or `src` parameter needs to be specified for URL generation. | +| transformation | Optional. An array of objects specifying the transformation to be applied in the URL. The transformation name and the value should be specified as a key-value pair in the object. Different steps of a [chained transformation](https://docs.imagekit.io/features/image-transformations/chained-transformations) can be specified as different objects of the array. The complete list of supported transformations in the SDK and some examples of using them are given later. If you use a transformation name that is not specified in the SDK, it gets applied as it is in the URL. | +| transformationPosition | Optional. The default value is `path` that places the transformation string as a path parameter in the URL. It can also be specified as `query`, which adds the transformation string as the URL's query parameter `tr`. If you use the `src` parameter to create the URL, then the transformation string is always added as a query parameter. | +| queryParameters | Optional. These are the other query parameters that you want to add to the final URL. These can be any query parameters and not necessarily related to ImageKit. Especially useful if you want to add some versioning parameter to your URLs. | +| signed | Optional. Boolean. Default is `false`. If set to `true`, the SDK generates a signed image URL adding the image signature to the image URL. If you create a URL using the `src` parameter instead of `path`, then do correct `urlEndpoint` for this to work. Otherwise returned URL will have the wrong signature | +| expireSeconds | Optional. Integer. Meant to be used along with the `signed` parameter to specify the time in seconds from now when the URL should expire. If specified, the URL contains the expiry timestamp in the URL, and the image signature is modified accordingly. | #### Examples of generating URLs **1. Chained Transformations as a query parameter** + ```js var imageURL = imagekit.url({ - path : "/default-image.jpg", - urlEndpoint : "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation : [{ - "height" : "300", - "width" : "400" - }, { - "rotation" : 90 - }], - transformationPosition : "query" + 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 ``` @@ -147,35 +135,42 @@ There are some transforms like [Sharpening](https://docs.imagekit.io/features/im ```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" - }] + src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", + transformation: [ + { + format: "jpg", + progressive: "true", + effectSharpen: "-", + effectContrast: "1", + }, + ], }); ``` + ``` //Note that because the `src` parameter was used, the transformation string gets added as a query parameter `tr` https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=f-jpg%2Cpr-true%2Ce-sharpen%2Ce-contrast-1 ``` **3. Signed URL that expires in 300 seconds with the default URL endpoint and other query parameters** + ```js var imageURL = imagekit.url({ - path : "/default-image.jpg", - queryParameters : { - "v" : "123" + path: "/default-image.jpg", + queryParameters: { + v: "123", }, - transformation : [{ - "height" : "300", - "width" : "400" - }], - signed : true, - expireSeconds : 300 + transformation: [ + { + height: "300", + width: "400", + }, + ], + signed: true, + expireSeconds: 300, }); ``` + ``` https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400/default-image.jpg?v=123&ik-t=1567358667&ik-s=f2c7cdacbe7707b71a83d49cf1c6110e3d701054 ``` @@ -200,7 +195,9 @@ var imageURL = imagekit.url({ }] }); ``` + **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 ``` @@ -221,7 +218,9 @@ var imageURL = imagekit.url({ }] }); ``` + **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 ``` @@ -242,7 +241,9 @@ var imageURL = imagekit.url({ }] }); ``` + **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 ``` @@ -256,62 +257,61 @@ For example: ```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" - }] + transformation: [ + { + width: "iw_div_4", + height: "ih_div_2", + border: "cw_mul_0.05_yellow", + }, + ], }); ``` **Sample Result URL** + ``` https://ik.imagekit.io/your_imagekit_id/tr:w-iw_div_4,h-ih_div_2,b-cw_mul_0.05_yellow/default-image.jpg ``` - #### 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` | - - +| 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 @@ -320,6 +320,7 @@ The SDK provides a simple interface using the `.upload()` method to upload files 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 @@ -381,8 +382,6 @@ imagekit.upload({ 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. - - ## File Management 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. @@ -394,25 +393,30 @@ Accepts an object specifying the parameters used to list and search files. All p ```js // Using Callback Function -imagekit.listFiles({ - skip : 10, - limit : 10 -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - +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); -}); +imagekit + .listFiles({ + skip: 10, + limit: 10, + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Get File Details** @@ -499,34 +503,40 @@ Note: If `publish` is included in the update options, no other parameters are al ```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); -}); - +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); -}); +imagekit + .updateFileDetails("file_id", { + publish: { + isPublished: true, + includeFileVersions: true, + }, + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Bulk Add tags** @@ -536,18 +546,21 @@ Add tags to multiple files in a single request as per [API documentation here](h ```js // Using Callback Function -imagekit.bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"], function(error, result) { - if(error) console.log(error); +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); -}); +imagekit + .bulkAddTags(["file_id_1", "file_id_2"], ["tag1", "tag2"]) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Bulk Remove tags** @@ -557,18 +570,21 @@ Remove tags from multiple files in a single request as per [API documentation he ```js // Using Callback Function -imagekit.bulkRemoveTags(["file_id_1", "file_id_2"], ["tags"], function(error, result) { - if(error) console.log(error); +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); -}); +imagekit + .bulkRemoveTags(["file_id_1", "file_id_2"], ["tags"]) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Bulk Remove AI Tags** @@ -578,18 +594,21 @@ Remove AI tags from multiple files in a single request as per [API documentation ```js // Using Callback Function -imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"], function(error, result) { - if(error) console.log(error); +imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"], function (error, result) { + if (error) console.log(error); else console.log(result); }); // Using Promises -imagekit.bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .bulkRemoveAITags(["file_id_1", "file_id_2"], ["ai-tag1", "ai-tag2"]) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Delete File** @@ -599,19 +618,21 @@ Delete a file as per the [API documentation here](https://docs.imagekit.io/api-r ```js // Using Callback Function -imagekit.deleteFile("file_id", function(error, result) { - if(error) console.log(error); +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); -}); +imagekit + .deleteFile("file_id") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Delete File Version** @@ -621,25 +642,30 @@ Delete any non-current version of a file as per the [API documentation here](htt ```js // Using Callback Function -imagekit.deleteFileVersion({ - fileId: "file_id", - versionId: "version_id", -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); - +imagekit.deleteFileVersion( + { + fileId: "file_id", + versionId: "version_id", + }, + function (error, result) { + if (error) console.log(error); + else console.log(result); + }, +); // Using Promises -imagekit.deleteFile({ - fileId: "file_id", - versionId: "version_id", -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .deleteFile({ + fileId: "file_id", + versionId: "version_id", + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Bulk Delete Files** @@ -649,19 +675,21 @@ Delete multiple files as per the [API documentation here](https://docs.imagekit. ```js // Using Callback Function -imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"], function(error, result) { - if(error) console.log(error); +imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"], function (error, result) { + if (error) console.log(error); else console.log(result); }); - // Using Promises -imagekit.bulkDeleteFiles(["file_id_1", "file_id_2"]).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .bulkDeleteFiles(["file_id_1", "file_id_2"]) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Copy File** @@ -700,24 +728,30 @@ This will move a file from one location to another as per [API documentation her ```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); -}); +imagekit.moveFile( + { + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/copy/into/", + }, + function (error, result) { + if (error) console.log(error); + else console.log(result); + }, +); // Using Promises -imagekit.moveFile({ - sourceFilePath: "/path/to/file.jpg", - destinationPath: "/folder/to/copy/into/", -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .moveFile({ + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/copy/into/", + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Rename File** @@ -727,26 +761,32 @@ Rename the file as per [API documentation here](https://docs.imagekit.io/api-ref ```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); -}); +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 -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); -}); +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); + }); ``` **Restore File Version** @@ -756,24 +796,30 @@ Restore the file version as per [API documentation here](https://docs.imagekit.i ```js // Using Callback Function -imagekit.restoreFileVersion({ - fileId: "file_id", - versionId: "version_id" -}, function(error, result) { - if(error) console.log(error); - else console.log(result); -}); +imagekit.restoreFileVersion( + { + fileId: "file_id", + versionId: "version_id", + }, + function (error, result) { + if (error) console.log(error); + else console.log(result); + }, +); // Using Promises -imagekit.restoreFileVersion({ - fileId: "file_id", - versionId: "version_id" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .restoreFileVersion({ + fileId: "file_id", + versionId: "version_id", + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Create Folder** @@ -783,24 +829,30 @@ This will create a new folder as per [API documentation here](https://docs.image ```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); -}); +imagekit.createFolder( + { + folderName: "new_folder", + parentFolderPath: "source/folder/path", + }, + function (error, result) { + if (error) console.log(error); + else console.log(result); + }, +); // Using Promises -imagekit.createFolder({ - folderName: "new_folder", - parentFolderPath: "source/folder/path" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .createFolder({ + folderName: "new_folder", + parentFolderPath: "source/folder/path", + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Delete Folder** @@ -810,18 +862,21 @@ This will delete the specified Folder and all nested files & folders as per [API ```js // Using Callback Function -imagekit.deleteFolder("folderPath", function(error, result) { - if(error) console.log(error); +imagekit.deleteFolder("folderPath", function (error, result) { + if (error) console.log(error); else console.log(result); }); // Using Promises -imagekit.deleteFolder("folderPath").then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .deleteFolder("folderPath") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Copy Folder** @@ -831,26 +886,32 @@ This will copy one Folder into another as per [API documentation here](https://d ```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); -}); +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); + }, +); // Using Promises -imagekit.copyFolder({ - sourceFolderPath: "/folder/to/copy", - destinationPath: "/folder/to/copy/into/", - includeFileVersions: false // optional -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .copyFolder({ + sourceFolderPath: "/folder/to/copy", + destinationPath: "/folder/to/copy/into/", + includeFileVersions: false, // optional + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Move Folder** @@ -860,24 +921,30 @@ This will move one Folder into another as per [API documentation here](https://d ```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); -}); +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 -imagekit.moveFolder({ - sourceFolderPath: "/folder/to/move", - destinationPath: "/folder/to/move/into/" -}).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .moveFolder({ + sourceFolderPath: "/folder/to/move", + destinationPath: "/folder/to/move/into/", + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Get bulk job status** @@ -887,18 +954,21 @@ This allows us to get a bulk operation status e.g. copy or move Folder as per [A ```js // Using Callback Function -imagekit.getBulkJobStatus("jobId", function(error, result) { - if(error) console.log(error); +imagekit.getBulkJobStatus("jobId", function (error, result) { + if (error) console.log(error); else console.log(result); }); // Using Promises -imagekit.getBulkJobStatus("jobId").then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .getBulkJobStatus("jobId") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Purge Cache** @@ -908,19 +978,21 @@ Programmatically issue a clear cache request as per the [API documentation here] ```js // Using Callback Function -imagekit.purgeCache("full_url", function(error, result) { - if(error) console.log(error); +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); -}); +imagekit + .purgeCache("full_url") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Purge Cache Status** @@ -930,19 +1002,21 @@ Get the purge cache request status using the request ID returned when a purge ca ```js // Using Callback Function -imagekit.getPurgeCacheStatus("cache_request_id", function(error, result) { - if(error) console.log(error); +imagekit.getPurgeCacheStatus("cache_request_id", function (error, result) { + if (error) console.log(error); else console.log(result); }); - // Using Promises -imagekit.getPurgeCacheStatus("cache_request_id").then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .getPurgeCacheStatus("cache_request_id") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Get File Metadata** @@ -999,33 +1073,33 @@ imagekit.createCustomMetadataField( schema: { type: "Number", minValue: 1000, - maxValue: 3000 - } + maxValue: 3000, + }, }, - function(error, result) { - if(error) console.log(error); + function (error, result) { + if (error) console.log(error); else console.log(result); - } + }, ); - // Using Promises -imagekit.createCustomMetadataField( - { +imagekit + .createCustomMetadataField({ name: "price", label: "price", schema: { type: "Number", minValue: 1000, - maxValue: 3000 - } - } -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); + maxValue: 3000, + }, + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Get all custom metadata fields** @@ -1037,26 +1111,26 @@ Get the list of all custom metadata fields as per the [API documentation here](h imagekit.getCustomMetadataFields( { - includeDeleted: false // optional + includeDeleted: false, // optional }, - function(error, result) { - if(error) console.log(error); + function (error, result) { + if (error) console.log(error); else console.log(result); - } + }, ); - // Using Promises -imagekit.getCustomMetadataFields( - { - includeDeleted: false // optional - } -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .getCustomMetadataFields({ + includeDeleted: false, // optional + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Update a custom metadata field** @@ -1071,31 +1145,30 @@ imagekit.updateCustomMetadataField( { schema: { minValue: 500, - maxValue: 2500 - } + maxValue: 2500, + }, }, - function(error, result) { - if(error) console.log(error); + function (error, result) { + if (error) console.log(error); else console.log(result); - } + }, ); - // Using Promises -imagekit.updateCustomMetadataField( - "field_id", - { +imagekit + .updateCustomMetadataField("field_id", { schema: { minValue: 500, - maxValue: 2500 - } - }, -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); + maxValue: 2500, + }, + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` **Delete a custom metadata field** @@ -1105,24 +1178,21 @@ delete a custom metadata field as per the [API documentation here](https://docs. ```js // Using Callback Function -imagekit.deleteCustomMetadataField( - "field_id", - function(error, result) { - if(error) console.log(error); - else console.log(result); - } -); - +imagekit.deleteCustomMetadataField("field_id", function (error, result) { + if (error) console.log(error); + else console.log(result); +}); // Using Promises -imagekit.deleteCustomMetadataField( - "field_id" -).then(response => { - console.log(response); -}).catch(error => { - console.log(error); -}); +imagekit + .deleteCustomMetadataField("field_id") + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); ``` ## Utility functions @@ -1133,13 +1203,14 @@ We have included the following commonly used utility functions in this package. 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* +_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", @@ -1165,21 +1236,24 @@ const calculateDistance = () => { // Calculate the distance between them: const distance = imagekit.pHashDistance(firstHash, secondHash); return distance; -} +}; ``` + #### Distance calculation examples ```js -imagekit.pHashDistance('f06830ca9f1e3e90', 'f06830ca9f1e3e90'); +imagekit.pHashDistance("f06830ca9f1e3e90", "f06830ca9f1e3e90"); // output: 0 (same image) -imagekit.pHashDistance('2d5ad3936d2e015b', '2d6ed293db36a4fb'); +imagekit.pHashDistance("2d5ad3936d2e015b", "2d6ed293db36a4fb"); // output: 17 (similar images) -imagekit.pHashDistance('a4a65595ac94518b', '7838873e791f8400'); +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 @@ -1202,15 +1276,16 @@ try { ``` ## 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. | +| 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 @@ -1221,22 +1296,22 @@ Verifying webhook signature is easy with imagekit SDK. All you need is the value Here is an example using the express.js server. ```js -const express = require('express'); -const Imagekit = require('imagekit'); +const express = require("express"); +const Imagekit = require("imagekit"); // Webhook configs -const WEBHOOK_SECRET = 'whsec_...'; // Copy from Imagekit dashboard +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_...', -}) + publicKey: "public_...", + urlEndpoint: "https://ik.imagekit.io/imagekit_id", + privateKey: "private_...", +}); const app = express(); -app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { +app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-ik-signature"]; const requestBody = req.body; let webhookResult; @@ -1257,13 +1332,13 @@ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { // Handle webhook switch (event.type) { - case 'video.transformation.accepted': + 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': + 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': + 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: @@ -1273,11 +1348,11 @@ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { // Return a response to acknowledge receipt of the event res.send(); -}) +}); app.listen(3000, () => { - console.log(`Example app listening on port 3000`) -}) + console.log(`Example app listening on port 3000`); +}); ``` ## Support @@ -1285,9 +1360,162 @@ app.listen(3000, () => { For any feedback or to report any issues or general implementation support, please reach out to [support@imagekit.io](mailto:support@imagekit.io) ## Links -* [Documentation](https://docs.imagekit.io) -* [Main website](https://imagekit.io) + +- [Documentation](https://docs.imagekit.io) +- [Main website](https://imagekit.io) ## License Released under the MIT license. + +## Reference + +A full reference for this library is available [here](https://github.com/imagekit-developer/imagekit-nodejs/blob/HEAD/./reference.md). + +## Request And Response Types + +The SDK exports all request and response types as TypeScript interfaces. Simply import them with the +following namespace: + +```typescript +import { ImageKit } from "imagekit"; + +const request: ImageKit.FileUploadV1 = { + ... +}; +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```typescript +import { ImageKitError } from "imagekit"; + +try { + await client.files.upload(...); +} catch (err) { + if (err instanceof ImageKitError) { + console.log(err.statusCode); + console.log(err.message); + console.log(err.body); + console.log(err.rawResponse); + } +} +``` + +## Advanced + +### Additional Headers + +If you would like to send additional headers as part of the request, use the `headers` request option. + +```typescript +const response = await client.files.upload(..., { + headers: { + 'X-Custom-Header': 'custom value' + } +}); +``` + +### Additional Query String Parameters + +If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. + +```typescript +const response = await client.files.upload(..., { + queryParams: { + 'customQueryParamKey': 'custom query param value' + } +}); +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `maxRetries` request option to configure this behavior. + +```typescript +const response = await client.files.upload(..., { + maxRetries: 0 // override maxRetries at the request level +}); +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. + +```typescript +const response = await client.files.upload(..., { + timeoutInSeconds: 30 // override timeout to 30s +}); +``` + +### Aborting Requests + +The SDK allows users to abort requests at any point by passing in an abort signal. + +```typescript +const controller = new AbortController(); +const response = await client.files.upload(..., { + abortSignal: controller.signal +}); +controller.abort(); // aborts the request +``` + +### Access Raw Response Data + +The SDK provides access to raw response data, including headers, through the `.withRawResponse()` method. +The `.withRawResponse()` method returns a promise that results to an object with a `data` and a `rawResponse` property. + +```typescript +const { data, rawResponse } = await client.files.upload(...).withRawResponse(); + +console.log(data); +console.log(rawResponse.headers['X-My-Header']); +``` + +### Runtime Compatibility + +The SDK works in the following runtimes: + +- Node.js 18+ +- Vercel +- Cloudflare Workers +- Deno v1.25+ +- Bun 1.0+ +- React Native + +### Customizing Fetch Client + +The SDK provides a way for you to customize the underlying HTTP client / Fetch function. If you're running in an +unsupported environment, this provides a way for you to break glass and ensure the SDK works. + +```typescript +import { ImageKitClient } from "imagekit"; + +const client = new ImageKitClient({ + ... + fetcher: // provide your implementation here +}); +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! diff --git a/babel-register.js b/babel-register.js deleted file mode 100644 index a24dfd20..00000000 --- a/babel-register.js +++ /dev/null @@ -1,3 +0,0 @@ -const register = require("@babel/register").default; - -register({ extensions: [".ts", ".tsx", ".js", ".jsx"] }); diff --git a/index.ts b/index.ts deleted file mode 100644 index 59d984fc..00000000 --- a/index.ts +++ /dev/null @@ -1,681 +0,0 @@ -/* - Helper Modules -*/ -import _ from "lodash"; -import errorMessages from "./libs/constants/errorMessages"; -import { - BulkDeleteFilesError, - BulkDeleteFilesResponse, - CopyFolderError, - CopyFolderResponse, - FileDetailsOptions, - FileObject, - FolderObject, - FileMetadataResponse, - ImageKitOptions, - ListFileOptions, - ListFileResponse, - MoveFolderError, - MoveFolderResponse, - PurgeCacheResponse, - PurgeCacheStatusResponse, - UploadOptions, - UploadResponse, - UrlOptions, - CopyFileOptions, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - FileVersionDetailsOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - CreateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - RenameFileOptions, - RenameFileResponse, -} from "./libs/interfaces"; -import { IKCallback } from "./libs/interfaces/IKCallback"; -import manage from "./libs/manage"; -import signature from "./libs/signature"; -import upload from "./libs/upload"; -import { verify as verifyWebhookEvent } from "./utils/webhook-signature"; -import customMetadataField from "./libs/manage/custom-metadata-field"; -/* - Implementations -*/ -import url from "./libs/url"; -/* - Utils -*/ -import pHashUtils from "./utils/phash"; -import transformationUtils from "./utils/transformation"; -import IKResponse from "./libs/interfaces/IKResponse"; - -const promisify = function (thisContext: ImageKit, fn: Function) { - return function (...args: any[]): Promise | void { - if (args.length === fn.length && typeof args[args.length - 1] !== "undefined") { - if (typeof args[args.length - 1] !== "function") { - throw new Error("Callback must be a function."); - } - fn.call(thisContext, ...args); - } else { - return new Promise((resolve, reject) => { - const callback = function (err: Error, ...results: any[]) { - if (err) { - return reject(err); - } else { - resolve(results.length > 1 ? results : results[0]); - } - }; - args.pop() - args.push(callback); - fn.call(thisContext, ...args); - }); - } - }; -}; -class ImageKit { - options: ImageKitOptions = { - uploadEndpoint: "https://upload.imagekit.io/api/v1/files/upload", - publicKey: "", - privateKey: "", - urlEndpoint: "", - transformationPosition: transformationUtils.getDefault(), - }; - - constructor(opts: ImageKitOptions = {} as ImageKitOptions) { - this.options = _.extend(this.options, opts); - if (!this.options.publicKey) { - throw new Error(errorMessages.MANDATORY_PUBLIC_KEY_MISSING.message); - } - if (!this.options.privateKey) { - throw new Error(errorMessages.MANDATORY_PRIVATE_KEY_MISSING.message); - } - if (!this.options.urlEndpoint) { - throw new Error(errorMessages.MANDATORY_URL_ENDPOINT_KEY_MISSING.message); - } - } - - /** - * This method allows you to create an URL to access a file using the relative or absolute path and the ImageKit URL endpoint (urlEndpoint). The file can be an image, video or any other static file supported by ImageKit. - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#url-generation} - * @see {@link https://docs.imagekit.io/integration/url-endpoints} - * - * @param urlOptions - */ - - url(urlOptions: UrlOptions): string { - return url(urlOptions, this.options); - } - - /** - * You can upload file to ImageKit.io media library from your server-side using private API key authentication. - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload} - * - * @param uploadOptions - */ - upload(uploadOptions: UploadOptions): Promise>; - upload(uploadOptions: UploadOptions, callback: IKCallback>): void; - upload( - uploadOptions: UploadOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, upload)(uploadOptions, this.options, callback); - } - - /** - * This API can list all the uploaded files in your ImageKit.io media library. - * For searching and filtering, you can use query parameters as described in docs. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - * - * @param listFilesOptions - */ - listFiles(listOptions: ListFileOptions): Promise>; - listFiles(listOptions: ListFileOptions, callback: IKCallback>): void; - listFiles( - listOptions: ListFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.listFiles)(listOptions, this.options, callback); - } - - /** - * Get the file details such as tags, customCoordinates, and isPrivate properties using get file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-details} - * - * @param fileId - */ - getFileDetails(fileId: string): Promise>; - getFileDetails(fileId: string, callback: IKCallback>): void; - getFileDetails( - fileId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileDetails)(fileId, this.options, callback); - } - - /** - * Get all versions of an assset. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-versions} - * - * @param fileId - */ - getFileVersions(fileId: string): Promise>; - getFileVersions(fileId: string, callback: IKCallback>): void; - getFileVersions( - fileId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileVersions)(fileId, this.options, callback); - } - - /** - * Get file details of a specific version. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/get-file-version-details} - * - * @param fileVersionDetailsOptions - */ - getFileVersionDetails(fileVersionDetailsOptions: FileVersionDetailsOptions): Promise>; - getFileVersionDetails( - fileVersionDetailsOptions: FileVersionDetailsOptions, - callback: IKCallback>, - ): void; - getFileVersionDetails( - fileVersionDetailsOptions: FileVersionDetailsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileVersionDetails)( - fileVersionDetailsOptions, - this.options, - callback, - ); - } - - /** - * Get image exif, pHash and other metadata for uploaded files in ImageKit.io media library using this API. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files} - * - * @param fileIdOrURL The unique fileId of the uploaded file or absolute URL. - */ - getFileMetadata(fileIdOrURL: string): Promise>; - getFileMetadata(fileIdOrURL: string, callback: IKCallback>): void; - getFileMetadata( - fileIdOrURL: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getFileMetadata)( - fileIdOrURL, - this.options, - callback, - ); - } - - /** - * Update file details such as tags and customCoordinates attribute using update file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/update-file-details} - * - * @param fileId The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - * @param updateData - */ - updateFileDetails(fileId: string, updateData: FileDetailsOptions): Promise>; - updateFileDetails(fileId: string, updateData: FileDetailsOptions, callback: IKCallback>): void; - updateFileDetails( - fileId: string, - updateData: FileDetailsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.updateFileDetails)( - fileId, - updateData, - this.options, - callback, - ); - } - - /** - * Add tags to multiple files in a single request. The method accepts an array of fileIDs of the files and an array of tags that have to be added to those files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/add-tags-bulk} - * - * @param fileIds - * @param tags - */ - bulkAddTags(fileIds: string[], tags: string[]): Promise>; - bulkAddTags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkAddTags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkAddTags)(fileIds, tags, this.options, callback); - } - - /** - * Remove tags to multiple files in a single request. The method accepts an array of fileIDs of the files and an array of tags that have to be removed to those files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/remove-tags-bulk} - * - * @param fileIds - * @param tags - */ - bulkRemoveTags(fileIds: string[], tags: string[]): Promise>; - bulkRemoveTags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkRemoveTags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkRemoveTags)(fileIds, tags, this.options, callback); - } - - /** - * Remove AITags from multiple files in a single request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/remove-aitags-bulk} - * - * @param fileIds - * @param tags - */ - bulkRemoveAITags(fileIds: string[], tags: string[]): Promise>; - bulkRemoveAITags(fileIds: string[], tags: string[], callback: IKCallback>): void; - bulkRemoveAITags( - fileIds: string[], - tags: string[], - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.bulkRemoveAITags)(fileIds, tags, this.options, callback); - } - - /** - * You can programmatically delete uploaded files in media library using delete file API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-file} - * - * @param fileId The unique fileId of the uploaded file. fileId is returned in list files API and upload API - */ - deleteFile(fileId: string): Promise>; - deleteFile(fileId: string, callback: IKCallback>): void; - deleteFile(fileId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.deleteFile)(fileId, this.options, callback); - } - - /** - * Delete any non-current version of a file. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-file-version} - * - * @param deleteFileVersionOptions - */ - deleteFileVersion(deleteFileVersionOptions: DeleteFileVersionOptions): Promise>; - deleteFileVersion(deleteFileVersionOptions: DeleteFileVersionOptions, callback: IKCallback>): void; - deleteFileVersion( - deleteFileVersionOptions: DeleteFileVersionOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.deleteFileVersion)( - deleteFileVersionOptions, - this.options, - callback, - ); - } - - /** - * Restore file version to a different version of a file. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/restore-file-version} - * - * @param restoreFileVersionOptions - */ - restoreFileVersion(restoreFileVersionOptions: RestoreFileVersionOptions): Promise>; - restoreFileVersion( - restoreFileVersionOptions: RestoreFileVersionOptions, - callback: IKCallback>, - ): void; - restoreFileVersion( - restoreFileVersionOptions: RestoreFileVersionOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.restoreFileVersion)( - restoreFileVersionOptions, - this.options, - callback, - ); - } - - /** - * This will purge CDN and ImageKit.io internal cache. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache} - * - * @param url The exact URL of the file to be purged. For example - https://ik.imageki.io/your_imagekit_id/rest-of-the-file-path.jpg - */ - purgeCache(url: string): Promise>; - purgeCache(url: string, callback: IKCallback>): void; - purgeCache( - url: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.purgeCache)(url, this.options, callback); - } - - /** - * Get the status of submitted purge request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache-status} - * - * @param requestId The requestId returned in response of purge cache API. - */ - getPurgeCacheStatus(requestId: string, callback: IKCallback>): void; - getPurgeCacheStatus(requestId: string): Promise>; - getPurgeCacheStatus( - requestId: string, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.getPurgeCacheStatus)( - requestId, - this.options, - callback, - ); - } - - /** - * Delete multiple files. The method accepts an array of file IDs of the files that have to be deleted. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-files-bulk} - * - * @param fileIdArray The requestId returned in response of purge cache API. - */ - bulkDeleteFiles( - fileIdArray: string[], - callback?: IKCallback, IKResponse>, - ): void | Promise>; - bulkDeleteFiles( - fileIdArray: string[], - callback?: IKCallback, IKResponse>, - ): void | Promise> { - return promisify>(this, manage.bulkDeleteFiles)( - fileIdArray, - this.options, - callback, - ); - } - - /** - * This will copy a file from one location to another. This method accepts the source file's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-file} - * - * @param copyFileOptions - */ - copyFile(copyFileOptions: CopyFileOptions): Promise>; - copyFile(copyFileOptions: CopyFileOptions, callback: IKCallback>): void; - copyFile( - copyFileOptions: CopyFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.copyFile)(copyFileOptions, this.options, callback); - } - - /** - * This will move a file from one location to another. This method accepts the source file's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-file} - * - * @param moveFileOptions - */ - moveFile(moveFileOptions: MoveFileOptions): Promise>; - moveFile(moveFileOptions: MoveFileOptions, callback: IKCallback>): void; - moveFile( - moveFileOptions: MoveFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.moveFile)(moveFileOptions, this.options, callback); - } - - /** - * You can programmatically rename an already existing file in the media library using rename file API. This operation would rename all file versions of the file. Note: The old URLs will stop working. The file/file version URLs cached on CDN will continue to work unless a purge is requested. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - * - * @param renameFileOptions - */ - renameFile(renameFileOptions: RenameFileOptions): Promise>; - renameFile(renameFileOptions: RenameFileOptions, callback: IKCallback>): void; - renameFile( - renameFileOptions: RenameFileOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.renameFile)( - renameFileOptions, - this.options, - callback, - ); - } - - /** - * This will create a new folder. This method accepts folder name and parent folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/create-folder} - * - * @param createFolderOptions - */ - createFolder(createFolderOptions: CreateFolderOptions): Promise>; - createFolder(createFolderOptions: CreateFolderOptions, callback: IKCallback>): void; - createFolder( - createFolderOptions: CreateFolderOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, manage.createFolder)(createFolderOptions, this.options, callback); - } - - /** - * This will delete the specified folder and all nested files & folders. This method accepts the full path of the folder that is to be deleted. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-folder} - * - * @param foldePath - */ - deleteFolder(folderPath: string): Promise>; - deleteFolder(folderPath: string, callback: IKCallback>): void; - deleteFolder(folderPath: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.deleteFolder)(folderPath, this.options, callback); - } - - /** - * This will copy a folder from one location to another. This method accepts the source folder's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * @param copyFolderOptions - */ - copyFolder(copyFolderOptions: CopyFolderOptions): Promise>; - copyFolder( - copyFolderOptions: CopyFolderOptions, - callback: IKCallback, IKResponse>, - ): void; - copyFolder( - copyFolderOptions: CopyFolderOptions, - callback?: IKCallback, IKResponse>, - ): void | Promise> { - return promisify>(this, manage.copyFolder)( - copyFolderOptions, - this.options, - callback, - ); - } - - /** - * This will move a folder from one location to another. This method accepts the source folder's path and destination folder path. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * @param moveFolderOptions - */ - moveFolder(moveFolderOptions: MoveFolderOptions): Promise>; - moveFolder( - moveFolderOptions: MoveFolderOptions, - callback: IKCallback, IKResponse>, - ): void; - moveFolder( - moveFolderOptions: MoveFolderOptions, - callback?: IKCallback, MoveFolderError>, - ): void | Promise> { - return promisify>(this, manage.moveFolder)( - moveFolderOptions, - this.options, - callback, - ); - } - - /** - * In case you are looking to implement client-side file upload, you are going to need a token, expiry timestamp, and a valid signature for that upload. The SDK provides a simple method that you can use in your code to generate these authentication parameters for you. - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#authentication-parameter-generation} - * - * @param token - * @param expire - */ - getAuthenticationParameters(token?: string, expire?: number): { token: string; expire: number; signature: string } { - return signature.getAuthenticationParameters(token, expire, this.options); - } - - /** - * This allows us to get a bulk operation status e.g. copy or move folder. This method accepts jobId that is returned by copy and move folder operations. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * @param jobId - */ - getBulkJobStatus(jobId: string): Promise>; - getBulkJobStatus(jobId: string, callback: IKCallback>): Promise>; - getBulkJobStatus(jobId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, manage.getBulkJobStatus)(jobId, this.options, callback); - } - - /** - * Create custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field} - * - * @param createCustomMetadataFieldOptions - */ - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - ): Promise>; - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - callback: IKCallback>, - ): Promise>; - createCustomMetadataField( - createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.create)( - createCustomMetadataFieldOptions, - this.options, - callback, - ); - } - - /** - *Get a list of all the custom metadata fields. - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/get-custom-metadata-field} - * - */ - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - ): Promise>; - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - callback: IKCallback>, - ): Promise>; - getCustomMetadataFields( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.list)( - getCustomMetadataFieldsOptions, - this.options, - callback, - ); - } - - /** - * Update custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field} - * - * @param fieldId - * @param updateCustomMetadataFieldOptions - */ - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - ): Promise>; - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - callback: IKCallback>, - ): Promise>; - updateCustomMetadataField( - fieldId: string, - updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, - callback?: IKCallback>, - ): void | Promise> { - return promisify>(this, customMetadataField.update)( - fieldId, - updateCustomMetadataFieldOptions, - this.options, - callback, - ); - } - - /** - * Delete a custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/delete-custom-metadata-field} - * - * @param fieldId - */ - deleteCustomMetadataField(fieldId: string): Promise>; - deleteCustomMetadataField(fieldId: string, callback: IKCallback>): void; - deleteCustomMetadataField(fieldId: string, callback?: IKCallback>): void | Promise> { - return promisify>(this, customMetadataField.deleteField)(fieldId, this.options, callback); - } - - /** - * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on an image's contents. ImageKit.io metadata API returns the pHash value of an image in the response. You can use this value to find a duplicate (or similar) image by calculating the distance between the two images' pHash value. - * - * This SDK exposes pHashDistance function to calculate the distance between two pHash values. It accepts two pHash hexadecimal strings and returns a numeric value indicative of the level of difference between the two images. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#perceptual-hash-phash} - * - * @param firstPHash - * @param secondPHash - */ - pHashDistance(firstPHash: string, secondPHash: string): number | Error { - return pHashUtils.pHashDistance(firstPHash, secondPHash); - } - - /** - * @param payload - Raw webhook request body (Encoded as UTF8 string or Buffer) - * @param signature - Webhook signature as UTF8 encoded strings (Stored in `x-ik-signature` header of the request) - * @param secret - Webhook secret as UTF8 encoded string [Copy from ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks) - * @returns \{ `timestamp`: Verified UNIX epoch timestamp if signature, `event`: Parsed webhook event payload \} - */ - verifyWebhookEvent = verifyWebhookEvent; -} - -export = ImageKit; diff --git a/jest.config.mjs b/jest.config.mjs new file mode 100644 index 00000000..b6927007 --- /dev/null +++ b/jest.config.mjs @@ -0,0 +1,42 @@ +/** @type {import('jest').Config} */ +export default { + preset: "ts-jest", + testEnvironment: "node", + projects: [ + { + displayName: "unit", + preset: "ts-jest", + testEnvironment: "node", + moduleNameMapper: { + "^(\.{1,2}/.*)\.js$": "$1", + }, + roots: ["/tests"], + testPathIgnorePatterns: ["\.browser\.(spec|test)\.[jt]sx?$", "/tests/wire/"], + setupFilesAfterEnv: [], + }, + { + displayName: "browser", + preset: "ts-jest", + testEnvironment: "/tests/BrowserTestEnvironment.ts", + moduleNameMapper: { + "^(\.{1,2}/.*)\.js$": "$1", + }, + roots: ["/tests"], + testMatch: ["/tests/unit/**/?(*.)+(browser).(spec|test).[jt]s?(x)"], + setupFilesAfterEnv: [], + }, + , + { + displayName: "wire", + preset: "ts-jest", + testEnvironment: "node", + moduleNameMapper: { + "^(\.{1,2}/.*)\.js$": "$1", + }, + roots: ["/tests/wire"], + setupFilesAfterEnv: ["/tests/mock-server/setup.ts"], + }, + ], + workerThreads: false, + passWithNoTests: true, +}; diff --git a/libs/constants/errorMessages.ts b/libs/constants/errorMessages.ts deleted file mode 100644 index ba10e6cd..00000000 --- a/libs/constants/errorMessages.ts +++ /dev/null @@ -1,53 +0,0 @@ -export default { - "MANDATORY_INITIALIZATION_MISSING": { message: "Missing publicKey or privateKey or urlEndpoint during ImageKit initialization", help: "" }, - "MANDATORY_PUBLIC_KEY_MISSING": { message: "Missing publicKey during ImageKit initialization", help: "" }, - "MANDATORY_PRIVATE_KEY_MISSING": { message: "Missing privateKey during ImageKit initialization", help: "" }, - "MANDATORY_URL_ENDPOINT_KEY_MISSING": { message: "Missing urlEndpoint during ImageKit initialization", help: "" }, - "INVALID_TRANSFORMATION_POSITION": { message: "Invalid transformationPosition parameter", help: "" }, - "CACHE_PURGE_URL_MISSING": { message: "Missing URL parameter for this request", help: "" }, - "CACHE_PURGE_STATUS_ID_MISSING": { message: "Missing Request ID parameter for this request", help: "" }, - "FILE_ID_MISSING": { message: "Missing fileId parameter for this request", help: "" }, - "FILE_VERSION_ID_MISSING": { message: "Missing versionId parameter for this request", help: "" }, - "FILE_ID_OR_URL_MISSING": { message: "Pass either fileId or remote URL of the image as first parameter", help: "" }, - "INVALID_LIST_OPTIONS": { message: "Pass a valid JSON list options e.g. {skip: 10, limit: 100}.", help: "" }, - "UPDATE_DATA_MISSING": { message: "Missing file update data for this request", help: "" }, - "UPDATE_DATA_TAGS_INVALID": { message: "Invalid tags parameter for this request", help: "tags should be passed as null or an array like ['tag1', 'tag2']" }, - "UPDATE_DATA_COORDS_INVALID": { message: "Invalid customCoordinates parameter for this request", help: "customCoordinates should be passed as null or a string like 'x,y,width,height'" }, - "LIST_FILES_INPUT_MISSING": { message: "Missing options for list files", help: "If you do not want to pass any parameter for listing, pass an empty object" }, - "MISSING_UPLOAD_DATA": { message: "Missing data for upload", help: "" }, - "MISSING_UPLOAD_FILE_PARAMETER": { message: "Missing file parameter for upload", help: "" }, - "MISSING_UPLOAD_FILENAME_PARAMETER": { message: "Missing fileName parameter for upload", help: "" }, - "JOB_ID_MISSING": { message: "Missing jobId parameter", help: "" }, - "INVALID_DESTINATION_FOLDER_PATH": { message: "Invalid destinationPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_INCLUDE_VERSION": { message: "Invalid includeFileVersions value", help: "It should be a boolean" }, - "INVALID_SOURCE_FILE_PATH": { message: "Invalid sourceFilePath value", help: "It should be a string like /path/to/file.jpg'" }, - "INVALID_SOURCE_FOLDER_PATH": { message: "Invalid sourceFolderPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_FOLDER_NAME": { message: "Invalid folderName value", help: "" }, - "INVALID_PARENT_FOLDER_PATH": { message: "Invalid parentFolderPath value", help: "It should be a string like '/path/to/folder'" }, - "INVALID_FOLDER_PATH": { message: "Invalid folderPath value", help: "It should be a string like '/path/to/folder'" }, - // pHash errors - "INVALID_PHASH_VALUE": { message: "Invalid pHash value", help: "Both pHash strings must be valid hexadecimal numbers" }, - "MISSING_PHASH_VALUE": { message: "Missing pHash value", help: "Please pass two pHash values" }, - "UNEQUAL_STRING_LENGTH": { message: "Unequal pHash string length", help: "For distance calucation, the two pHash strings must have equal length" }, - //bulk delete errors - "INVALID_FILEIDS_VALUE": { message: "Invalid value for fileIds", help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." }, - "BULK_ADD_TAGS_INVALID": { message: "Invalid value for tags", help: "tags should be a non empty array of string like ['tag1', 'tag2']." }, - "BULK_AI_TAGS_INVALID": { message: "Invalid value for AITags", help: "AITags should be a non empty array of string like ['tag1', 'tag2']." }, - "CMF_NAME_MISSING": { message: "Missing name parameter for this request", help: "" }, - "CMF_LABEL_MISSING": { message: "Missing label parameter for this request", help: "" }, - "CMF_SCHEMA_MISSING": { message: "Missing schema parameter for this request", help: "" }, - "CMF_SCHEMA_INVALID": { message: "Invalid value for schema", help: "schema should have a mandatory type field." }, - "CMF_LABEL_SCHEMA_MISSING": { message: "Both label and schema is missing", help: "" }, - "CMF_FIELD_ID_MISSING": { message: "Missing fieldId parameter for this request", help: "" }, - "INVALID_FILE_PATH": { message: "Invalid value for filePath", help: "Pass the full path of the file. For example - /path/to/file.jpg" }, - "INVALID_NEW_FILE_NAME": { message: "Invalid value for newFileName. It should be a string.", help: "" }, - "INVALID_PURGE_CACHE": { message: "Invalid value for purgeCache. It should be boolean.", help: "" }, - // Webhook signature - "VERIFY_WEBHOOK_EVENT_SIGNATURE_INCORRECT": { message: "Incorrect signature", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_SIGNATURE_MISSING": { message: "Signature missing", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_TIMESTAMP_MISSING": { message: "Timestamp missing", help: "Please pass x-ik-signature header as utf8 string" }, - "VERIFY_WEBHOOK_EVENT_TIMESTAMP_INVALID": { message: "Timestamp invalid", help: "Please pass x-ik-signature header as utf8 string" }, - "INVALID_TRANSFORMATION": { message: "Invalid transformation parameter. Please include at least pre, post, or both.", help: ""}, - "INVALID_PRE_TRANSFORMATION": { message: "Invalid pre transformation parameter.", help: ""}, - "INVALID_POST_TRANSFORMATION": { message: "Invalid post transformation parameter.", help: ""}, -}; \ No newline at end of file diff --git a/libs/constants/supportedTransforms.ts b/libs/constants/supportedTransforms.ts deleted file mode 100644 index fb0e6498..00000000 --- a/libs/constants/supportedTransforms.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @see {@link https://docs.imagekit.io/features/image-transformations} - */ -const supportedTransforms = { - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#width-w} - */ - width: "w", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#height-h} - */ - height: "h", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#aspect-ratio-ar} - */ - aspectRatio: "ar", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#quality-q} - */ - quality: "q", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#crop-crop-modes-and-focus} - */ - crop: "c", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#crop-crop-modes-and-focus} - */ - cropMode: "cm", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#focus-fo} - */ - focus: "fo", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#examples-focus-using-cropped-image-coordinates} - */ - x: "x", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#examples-focus-using-cropped-image-coordinates} - */ - y: "y", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#format-f} - */ - format: "f", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#radius-r} - */ - radius: "r", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#background-color-bg} - */ - background: "bg", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#border-b} - */ - border: "b", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#rotate-rt} - */ - rotation: "rt", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#rotate-rt} - */ - rotate: "rt", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#blur-bl} - */ - blur: "bl", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#named-transformation-n} - */ - named: "n", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#progressive-image-pr} - */ - progressive: "pr", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#lossless-webp-and-png-lo} - */ - lossless: "lo", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#trim-edges-t} - */ - trim: "t", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#image-metadata-md} - */ - metadata: "md", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#color-profile-cp} - */ - colorProfile: "cp", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#default-image-di} - */ - defaultImage: "di", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#dpr-dpr} - */ - dpr: "dpr", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#sharpen-e-sharpen} - */ - effectSharpen: "e-sharpen", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#unsharp-mask-e-usm} - */ - effectUSM: "e-usm", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#contrast-stretch-e-contrast} - */ - effectContrast: "e-contrast", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#grayscale-e-grayscale} - */ - effectGray: "e-grayscale", - - /** - * @link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#shadow-e-shadow - */ - effectShadow: "e-shadow", - - /** - * @link https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation#gradient-e-gradient - */ - effectGradient: "e-gradient", - - /** - * @see {@link https://docs.imagekit.io/features/image-transformations/resize-crop-and-other-transformations#original-image-orig} - */ - original: "orig", -}; - -export default supportedTransforms as { [key: string]: string }; -export type SupportedTransformsParam = keyof typeof supportedTransforms; diff --git a/libs/interfaces/BulkDeleteFiles.ts b/libs/interfaces/BulkDeleteFiles.ts deleted file mode 100644 index 9329db3f..00000000 --- a/libs/interfaces/BulkDeleteFiles.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Response when deleting multiple files from the media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/delete-files-bulk} - */ -export interface BulkDeleteFilesResponse { - /** - * List of file ids of successfully deleted files - */ - successfullyDeletedFileIds: string[]; -} - -export interface BulkDeleteFilesError extends Error { - help: string; - missingFileIds: string[]; -} diff --git a/libs/interfaces/CopyFile.ts b/libs/interfaces/CopyFile.ts deleted file mode 100644 index eef7cab3..00000000 --- a/libs/interfaces/CopyFile.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface CopyFileOptions { - /** - * The full path of the file you want to copy. For example - /path/to/file.jpg - */ - sourceFilePath: string; - /** - * Full path to the folder you want to copy the above file into. For example - /folder/to/copy/into/ - */ - destinationPath: string; - /** - * Option to copy all versions of a file. By default, only the current version of the file is copied. When set to true, all versions of the file will be copied. - * Default value is false - */ - includeFileVersions?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/CopyFolder.ts b/libs/interfaces/CopyFolder.ts deleted file mode 100644 index c29d1a0f..00000000 --- a/libs/interfaces/CopyFolder.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Response when copying folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * On success, you will receive a jobId which can be used to get the copy operation's status. - */ -export interface CopyFolderResponse { - jobId: string; -} - -/** - * Error when copying folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - * - * If no files or folders are found at the specified sourceFolderPath then a error is returned. - */ -export interface CopyFolderError extends Error { - help: string; - message: string; - reason: string; -} - -/** - * Copy folder API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/copy-folder} - */ -export interface CopyFolderOptions { - /** - * The full path to the source folder you want to copy. For example - /path/of/source/folder. - */ - sourceFolderPath: string; - /** - * Full path to the destination folder where you want to copy the source folder into. For example - /path/of/destination/folder. - */ - destinationPath: string; - /** - * Option to copy all versions of files that are nested inside the selected folder. By default, only the current version of each file will be copied. When set to true, all versions of each file will be copied. - * Default value - false - */ - includeFileVersions?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/CreateFolder.ts b/libs/interfaces/CreateFolder.ts deleted file mode 100644 index 6f7c0025..00000000 --- a/libs/interfaces/CreateFolder.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface CreateFolderOptions { - /** - * The folder will be created with this name. All characters except alphabets and numbers (inclusive of unicode letters, marks, and numerals in other languages) will be replaced by an underscore i.e. _. - */ - folderName: string; - /** - * The folder where the new folder should be created, for root use / else the path e.g. containing/folder/. - * Note: If any folder(s) is not present in the parentFolderPath parameter, it will be automatically created. For example, if you pass /product/images/summer, then product, images, and summer folders will be created if they don't already exist. - */ - parentFolderPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/CustomMetatadaField.ts b/libs/interfaces/CustomMetatadaField.ts deleted file mode 100644 index 7efcee06..00000000 --- a/libs/interfaces/CustomMetatadaField.ts +++ /dev/null @@ -1,123 +0,0 @@ -type RequiredSchema = { - isValueRequired: true; - defaultValue: T; -} | { - isValueRequired?: false; - defaultValue?: T; -}; - -type CustomMetadataTextField = RequiredSchema & { - type: "Text"; - minLength?: number; - maxLength?: number; -}; - -type CustomMetadataTextareaField = RequiredSchema & { - type: "Textarea"; - minLength?: number; - maxLength?: number; -}; - -type CustomMetadataNumberField = RequiredSchema & { - type: "Number"; - minValue?: string | number; - maxValue?: string | number; -}; - -type CustomMetadataDateField = RequiredSchema & { - type: "Date"; - minValue?: string | number; - maxValue?: string | number; -}; - -type CustomMetadataBooleanField = RequiredSchema & { - type: "Boolean"; -}; - -type CustomMetadataSingleSelectField = RequiredSchema> & { - type: "SingleSelect"; - selectOptions: Array; -}; - -type CustomMetadataMultiSelectField = RequiredSchema> & { - type: "MultiSelect"; - selectOptions: Array; -}; - -export type CustomMetadataFieldSchema = - | CustomMetadataTextField - | CustomMetadataTextareaField - | CustomMetadataNumberField - | CustomMetadataDateField - | CustomMetadataBooleanField - | CustomMetadataSingleSelectField - | CustomMetadataMultiSelectField; - -export type CustomMetadataFieldSchemaMinusType = - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit - | Omit; - -/** - * Create a new custom metadata field - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/create-custom-metadata-field} - */ -export interface CreateCustomMetadataFieldOptions { - /** - * Name of the metadata field, unique across all (deleted or not deleted) custom metadata fields. - */ - name: string; - /** - * Label of the metadata field, unique across all non deleted custom metadata fields - */ - label: string; - /** - * An object that describes the rules for the custom metadata key. - */ - schema: CustomMetadataFieldSchema -} - -export interface CustomMetadataField { - id: string; - /** - * Name of the metadata field, unique across all (deleted or not deleted) custom metadata fields. - */ - name: string; - /** - * Label of the metadata field, unique across all non deleted custom metadata fields - */ - label: string; - /** - * An object that describes the rules for the custom metadata key. - */ - schema: CustomMetadataFieldSchema -} - -/** - * Update the label or schema of an existing custom metadata field. - * - * @see {@link https://docs.imagekit.io/api-reference/custom-metadata-fields-api/update-custom-metadata-field} - */ -export interface UpdateCustomMetadataFieldOptions { - /** - * Label of the metadata field, unique across all non deleted custom metadata fields. This parameter is required if schema is not provided. - */ - label?: string; - /** - * An object that describes the rules for the custom metadata key. This parameter is required if label is not provided. - * Note: type cannot be updated and will be ignored if sent with the schema. The schema will be validated as per the existing type. - */ - schema?: CustomMetadataFieldSchemaMinusType -} - -export interface GetCustomMetadataFieldsOptions { - /** - * Set it to true if you want to receive deleted fields as well in the API response. - */ - includeDeleted?: boolean; -} \ No newline at end of file diff --git a/libs/interfaces/FileDetails.ts b/libs/interfaces/FileDetails.ts deleted file mode 100644 index 358ac538..00000000 --- a/libs/interfaces/FileDetails.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { FileType } from "./FileType"; - -export interface EmbeddedMetadataValues { - [key: string]: - | string - | number - | boolean - | Date - | Array -} - -export interface AITagItem { - name: string - confidence: number - source: 'google-auto-tagging' | 'aws-auto-tagging' -} - -export interface CMValues { - [key: string]: | string - | number - | boolean - | Array -} - -interface BgRemoval { - name: string - options: { - bg_color?: string - bg_image_url?: string - add_shadow: boolean - semitransparency: boolean - } -} - -interface AutoTag { - name: string - maxTags: number - minConfidence: number -} - -export type Extension = (BgRemoval | AutoTag)[]; - -/** - * Options when updating file details such as tags and customCoordinates attribute using update file detail API. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/update-file-details} - */ -export interface FileDetailsOptions { - /** - * Array of tags associated with the file. - */ - tags?: string[]; - /** - * Define an important area in the image. - * Example - 50,50,500,500 - */ - customCoordinates?: string; - /* - * Object with array of extensions to be processed on the image. - */ - extensions?: Extension; - /* - * Final status of pending extensions will be sent to this URL. - */ - webhookUrl?: string - /* - * Array of AI tags to remove from the asset. - */ - removeAITags?: string[]; - /* - * A key-value data to be associated with the asset. To unset a key, send null value for that key. Before setting any custom metadata on an asset you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /** - * Configure the publication status of a file and its versions. - */ - publish?: { - isPublished: boolean; - includeFileVersions?: boolean; - }; -} - -/** - * - * File object. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api#file-object-structure} - */ -export interface FileObject { - /** - * The unique fileId of the uploaded file. - */ - fileId: string; - /** - * Type of item. It can be either file, file-version or folder. - */ - type: "file" | "file-version"; - /** - * Name of the file or folder. - */ - name: string; - /** - * The relative path of the file. In case of image, you can use this - * path to construct different transformations. - */ - filePath: string; - /** - * Array of tags associated with the image. If no tags are set, it will be null. - */ - tags?: string[] | null; - /** - * Is the file marked as private. It can be either true or false. - */ - isPrivateFile: boolean; - /** - * Value of custom coordinates associated with the image in format x,y,width,height. - * If customCoordinates are not defined then it is null. - */ - customCoordinates: string | null; - /** - * A publicly accessible URL of the file. - */ - url: string; - /** - * In case of an image, a small thumbnail URL. - */ - thumbnail: string; - /** - * The type of file, it could be either image or non-image. - */ - fileType: FileType; - /* - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. - */ - AITags?: AITagItem[]; - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } - /* - * Consolidated embedded metadata associated with the file. It includes exif, iptc, and xmp data. - */ - embeddedMetadata?: EmbeddedMetadataValues | null; - /* - * A key-value data associated with the asset. Before setting any custom metadata on an asset, you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /* - * Size of the file in bytes - */ - size: number; - /* - * The date and time when the file was first uploaded. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - createdAt: string; - /* - * The date and time when the file was last updated. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - updatedAt: string; - /* - * Height of the image in pixels (Only for images) - */ - height: number; - /* - * Width of the image in pixels (Only for Images) - */ - width: number; - /* - * A boolean indicating if the image has an alpha layer or not. - */ - hasAlpha: boolean; - /* - * MIME Type of the file. For example - image/jpeg - */ - mime?: string; - /** - * An object containing the file or file version's id (versionId) and name. - */ - versionInfo?: { name: string; id: string }; -} - -/** - * - * Folder object. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api#file-object-structure} - */ -export interface FolderObject { - /** - * The unique fileId of the folder. - */ - folderId: string; - /** - * Type of item. It can be either file, file-version or folder. - */ - type: "folder"; - /** - * Name of the file or folder. - */ - name: string; - /** - * The relative path of the folder. - */ - folderPath: string; - /* - * The date and time when the folder was first created. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - createdAt: string; - /* - * The date and time when the folder was last updated. The format is YYYY-MM-DDTHH:mm:ss.sssZ - */ - updatedAt: string; -} - -export interface FileVersionDetailsOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} diff --git a/libs/interfaces/FileFormat.ts b/libs/interfaces/FileFormat.ts deleted file mode 100644 index aaa9bc90..00000000 --- a/libs/interfaces/FileFormat.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @see {@link https://help.imagekit.io/en/articles/2434102-image-format-support-in-imagekit-for-resizing-compression-and-static-file-delivery} - */ -export type FileFormat = - | "jpg" - | "png" - | "gif" - | "svg" - | "webp" - | "pdf" - | "js" - | "css" - | "txt" - | "mp4" - | "webm" - | "mov" - | "swf" - | "ts" - | "m3u8" - | string; diff --git a/libs/interfaces/FileMetadata.ts b/libs/interfaces/FileMetadata.ts deleted file mode 100644 index 642310fe..00000000 --- a/libs/interfaces/FileMetadata.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { FileFormat } from "./FileFormat"; - -/** - * Response when getting image exif, pHash and other metadata for uploaded files in ImageKit.io media library using this API. - * - * @see {@link https://docs.imagekit.io/api-reference/metadata-api/get-image-metadata-for-uploaded-media-files} - */ -export interface FileMetadataResponse { - height: number; - width: number; - size: number; - format: FileFormat; - hasColorProfile: boolean; - quality: number; - density: number; - hasTransparency: boolean; - /** - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#perceptual-hash-phash} - */ - pHash: string; - /** - * @see {@link https://docs.imagekit.io/api-reference/metadata-api#exif} - */ - exif: { - image: { - Make: string; - Model: string; - Orientation: number; - XResolution: number; - YResolution: number; - ResolutionUnit: number; - Software: string; - ModifyDate: string; - YCbCrPositioning: number; - ExifOffset: number; - GPSInfo: number; - }; - thumbnail: { - Compression: number; - XResolution: number; - YResolution: number; - ResolutionUnit: number; - ThumbnailOffset: number; - ThumbnailLength: number; - }; - exif: { - ExposureTime: number; - FNumber: number; - ExposureProgram: number; - ISO: number; - ExifVersion: string; - DateTimeOriginal: string; - CreateDate: string; - ShutterSpeedValue: number; - ApertureValue: number; - ExposureCompensation: number; - MeteringMode: number; - Flash: number; - FocalLength: number; - SubSecTime: string; - SubSecTimeOriginal: string; - SubSecTimeDigitized: string; - FlashpixVersion: string; - ColorSpace: number; - ExifImageWidth: number; - ExifImageHeight: number; - InteropOffset: number; - FocalPlaneXResolution: number; - FocalPlaneYResolution: number; - FocalPlaneResolutionUnit: number; - CustomRendered: number; - ExposureMode: number; - WhiteBalance: number; - SceneCaptureType: number; - }; - gps: { - GPSVersionID: number[]; - }; - interoperability: { - InteropIndex: string; - InteropVersion: string; - }; - makernote: { [key: string]: string }; - }; -} diff --git a/libs/interfaces/FileType.ts b/libs/interfaces/FileType.ts deleted file mode 100644 index f6b6cb70..00000000 --- a/libs/interfaces/FileType.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Type of files to include in result set. Accepts three values: - * all - include all types of files in result set - * image - only search in image type files - * non-image - only search in files which are not image, e.g., JS or CSS or video files. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - */ -export type FileType = "all" | "image" | "non-image"; diff --git a/libs/interfaces/FileVersion.ts b/libs/interfaces/FileVersion.ts deleted file mode 100644 index b2c489e1..00000000 --- a/libs/interfaces/FileVersion.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface DeleteFileVersionOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} - -export interface RestoreFileVersionOptions { - /** - * The unique fileId of the uploaded file. fileId is returned in list files API and upload API. - */ - fileId: string; - /** - * The unique versionId of the uploaded file's version. This is returned in list files API and upload API as id within the versionInfo parameter. - */ - versionId: string; -} \ No newline at end of file diff --git a/libs/interfaces/IKCallback.ts b/libs/interfaces/IKCallback.ts deleted file mode 100644 index b33ae426..00000000 --- a/libs/interfaces/IKCallback.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IKCallback { - (err: E | null, response: T): void; - (err: E, response: T | null): void; -} diff --git a/libs/interfaces/IKResponse.ts b/libs/interfaces/IKResponse.ts deleted file mode 100644 index a53ca4fb..00000000 --- a/libs/interfaces/IKResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -interface ResponseMetadata { - statusCode: number; - headers: Record; -} - -type IKResponse = T extends Error - ? T & { $ResponseMetadata?: ResponseMetadata } - : T & { $ResponseMetadata: ResponseMetadata }; - -export default IKResponse; diff --git a/libs/interfaces/ImageKitOptions.ts b/libs/interfaces/ImageKitOptions.ts deleted file mode 100644 index 122a4de2..00000000 --- a/libs/interfaces/ImageKitOptions.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TransformationPosition } from "."; - -export interface ImageKitOptions { - uploadEndpoint?: string, - publicKey: string; - privateKey: string; - urlEndpoint: string; - transformationPosition?: TransformationPosition; -} diff --git a/libs/interfaces/ListFile.ts b/libs/interfaces/ListFile.ts deleted file mode 100644 index bba9321d..00000000 --- a/libs/interfaces/ListFile.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { FileObject, FolderObject } from "./FileDetails"; -import { FileType } from "./FileType"; - -/** - * List and search files options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files} - */ - -export interface ListFileOptions { - /** - * Folder path if you want to limit the search within a specific folder. For example, /sales-banner/ will only search in folder sales-banner. - */ - path?: string; - /** - * Type of files to include in result set. Accepts three values: - * all - include all types of files in result set - * image - only search in image type files - * non-image - only search in files which are not image, e.g., JS or CSS or video files. - */ - fileType?: FileType; - /** - * Comma-separated list of tags. Files matching any of the tags are included in result response. If no tag is matched, the file is not included in result set. - */ - tags?: string | string[]; - /** - * Whether to include folders in search results or not. By default only files are searched. - * Accepts true and false. If this is set to true then tags and fileType parameters are ignored. - */ - includeFolder?: boolean; - /** - * The name of the file or folder. - */ - name?: string; - /** - * The maximum number of results to return in response: - * Minimum value - 1 - * Maximum value - 1000 - * Default value - 1000 - */ - limit?: number; - /** - * The number of results to skip before returning results. - * Minimum value - 0 - * Default value - 0 - */ - skip?: number; - /** - * You can sort based on the following fields: - * - name - ASC_NAME or DESC_NAME - * - createdAt - ASC_CREATED or DESC_CREATED - * - updatedAt - ASC_UPDATED or DESC_UPDATED - * - height - ASC_HEIGHT or DESC_HEIGHT - * - width - ASC_WIDTH or DESC_WIDTH - * - size - ASC_SIZE or DESC_SIZE - */ - sort?: string; - /** - * Limit search to either file or folder. Pass all to include both files and folders in search results. - * Default value - `file` - */ - type?: string; - /** - * Query string in a Lucene-like query language. Learn more about the query expression later in this section. - * Note: When the searchQuery parameter is present, the following query parameters will have no effect on the result: - * 1. tags - * 2. type - * 3. name - */ - searchQuery?: string; -} - -/** - * - * List and search response - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/list-and-search-files#response-structure-and-status-code-application-json} - */ -export type ListFileResponse = Array; diff --git a/libs/interfaces/MoveFile.ts b/libs/interfaces/MoveFile.ts deleted file mode 100644 index 1a26a162..00000000 --- a/libs/interfaces/MoveFile.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Move file API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-file} - */ -export interface MoveFileOptions { - /** - * The full path of the file you want to move. For example - /path/to/file.jpg - */ - sourceFilePath: string; - /** - * Full path to the folder you want to move the above file into. For example - /folder/to/move/into/ - */ - destinationPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/MoveFolder.ts b/libs/interfaces/MoveFolder.ts deleted file mode 100644 index 2361a6ed..00000000 --- a/libs/interfaces/MoveFolder.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Response when moving folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * On success, you will receive a jobId which can be used to get the move operation's status. - */ -export interface MoveFolderResponse { - jobId: string; -} - -/** - * Error when moving folder in media library. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - * - * If no files or folders are found at specified sourceFolderPath then a error is returned. - */ -export interface MoveFolderError extends Error { - help: string; - message: string; - reason: string; -} - - -/** - * Move folder API options - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/move-folder} - */ -export interface MoveFolderOptions { - /** - * The full path to the source folder you want to move. For example - /path/of/source/folder. - */ - sourceFolderPath: string; - /** - * Full path to the destination folder where you want to move the source folder into. For example - /path/of/destination/folder. - */ - destinationPath: string; -} \ No newline at end of file diff --git a/libs/interfaces/PurgeCache.ts b/libs/interfaces/PurgeCache.ts deleted file mode 100644 index c7b0e386..00000000 --- a/libs/interfaces/PurgeCache.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Response when purging CDN and ImageKit.io internal cache - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache#response-structure-and-status-code} - */ - -export interface PurgeCacheResponse { - /** - * requestId can be used to fetch the status of submitted purge request. - */ - requestId: string; -} - -/** - * Response when getting the status of submitted purge request. - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/purge-cache-status#understanding-response} - */ - -export interface PurgeCacheStatusResponse { - /** - * Pending - The request has been successfully submitted, and purging is in progress. - * Complete - The purge request has been successfully completed. And now you should get a fresh object. - * Check the Age header in response to confirm this. - */ - status: "Pending" | "Completed"; -} diff --git a/libs/interfaces/Rename.ts b/libs/interfaces/Rename.ts deleted file mode 100644 index 98b338df..00000000 --- a/libs/interfaces/Rename.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Response when rename file - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - */ -export interface RenameFileResponse { - /** - * When purgeCache is set to true - */ - purgeRequestId?: string; -} - -/** - * Response when rename file - * - * @see {@link https://docs.imagekit.io/api-reference/media-api/rename-file} - */ -export interface RenameFileOptions { - /** - * The full path of the file you want to rename. For example - /path/to/file.jpg - */ - filePath: string; - /** - * The new name of the file. A filename can contain: - - Alphanumeric Characters: a-z, A-Z, 0-9 (including Unicode letters, marks, and numerals in other languages). - - Special Characters: ., _, and -. Any other character, including space, will be replaced by _. - */ - newFileName: string - /** - * Option to purge cache for the old file URL. When set to true, it will internally issue a purge cache request on CDN to remove cached content on the old URL. - */ - purgeCache: boolean -} diff --git a/libs/interfaces/Transformation.ts b/libs/interfaces/Transformation.ts deleted file mode 100644 index a2990f66..00000000 --- a/libs/interfaces/Transformation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SupportedTransformsParam } from "../constants/supportedTransforms"; - -export type TransformationPosition = "path" | "query"; - -export type Transformation = Partial< - | { - [key in SupportedTransformsParam]: string | boolean | number; - } - | { [key: string]: string | boolean | number } ->; \ No newline at end of file diff --git a/libs/interfaces/UploadOptions.ts b/libs/interfaces/UploadOptions.ts deleted file mode 100644 index 47389b21..00000000 --- a/libs/interfaces/UploadOptions.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ReadStream } from "fs"; -import { Extension } from "./FileDetails"; - -interface TransformationObject { - type: "transformation"; - value: string; -} -interface GifToVideoOrThumbnailObject { - type: "gif-to-video" | "thumbnail"; - value?: string; -} - -interface AbsObject { - type: "abs"; - value: string; - protocol: "hls" | "dash"; -} - -type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; - -interface Transformation{ - pre?: string - post?: PostTransformation[] -} - -/** - * Options used when uploading a file - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload#request-structure-multipart-form-data} - */ -export interface UploadOptions { - /** - * This field accepts three kinds of values: - * - binary - You can send the content of the file as binary. This is used when a file is being uploaded from the browser. - * - base64 - Base64 encoded string of file content. - * - url - URL of the file from where to download the content before uploading. - * Downloading file from URL might take longer, so it is recommended that you pass the binary or base64 content of the file. - * Pass the full URL, for example - https://www.example.com/rest-of-the-image-path.jpg. - */ - file: string | Buffer | ReadStream; - /** - * The name with which the file has to be uploaded. - * The file name can contain: - * - Alphanumeric Characters: a-z , A-Z , 0-9 - * - Special Characters: . _ and - - * Any other character including space will be replaced by _ - */ - fileName: string; - /** - * Whether to use a unique filename for this file or not. - * - Accepts true or false. - * - If set true, ImageKit.io will add a unique suffix to the filename parameter to get a unique filename. - * - If set false, then the image is uploaded with the provided filename parameter and any existing file with the same name is replaced. - * Default value - true - */ - useUniqueFileName?: boolean; - /** - * Set the tags while uploading the file. - * - Comma-separated value of tags in format tag1,tag2,tag3. For example - t-shirt,round-neck,men - * - The maximum length of all characters should not exceed 500. - * - % is not allowed. - * - If this field is not specified and the file is overwritten then the tags will be removed. - */ - tags?: string | string[]; - /** - * The folder path (e.g. /images/folder/) in which the image has to be uploaded. If the folder(s) didn't exist before, a new folder(s) is created. - * The folder name can contain: - * - Alphanumeric Characters: a-z , A-Z , 0-9 - * - Special Characters: / _ and - - * - Using multiple / creates a nested folder. - * Default value - / - */ - folder?: string; - /** - * Whether to mark the file as private or not. This is only relevant for image type files. - * - Accepts true or false. - * - If set true, the file is marked as private which restricts access to the original image URL and unnamed image transformations without signed URLs. - * Without the signed URL, only named transformations work on private images - * Default value - false - */ - isPrivateFile?: boolean; - /** - * Define an important area in the image. This is only relevant for image type files. - * To be passed as a string with the x and y coordinates of the top-left corner, and width and height of the area of interest in format x,y,width,height. For example - 10,10,100,100 - * Can be used with fo-customtransformation. - * If this field is not specified and the file is overwritten, then customCoordinates will be removed. - */ - customCoordinates?: string; - /** - * Comma-separated values of the fields that you want ImageKit.io to return in response. - * - * For example, set the value of this field to tags,customCoordinates,isPrivateFile,metadata to get value of tags, customCoordinates, isPrivateFile , and metadata in the response. - */ - responseFields?: string | string[]; - /* - * Object with array of extensions to be processed on the image. - */ - extensions?: Extension; - /* - * Final status of pending extensions will be sent to this URL. - */ - webhookUrl?: string; - overwriteFile?: boolean; - overwriteAITags?: boolean; - overwriteTags?: boolean; - overwriteCustomMetadata?: boolean; - customMetadata?: { - [key: string]: string | number | boolean | Array; - }, - transformation?: Transformation - /** - * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. - */ - checks?: string - /** - * Optional. Determines whether the file should be uploaded as published. - * If set to false, the file will be marked as unpublished, restricting access to the file through the media library only. - * Files in draft or unpublished states can only be publicly accessed after they are published. - */ - isPublished?: boolean -} diff --git a/libs/interfaces/UploadResponse.ts b/libs/interfaces/UploadResponse.ts deleted file mode 100644 index 9ca44376..00000000 --- a/libs/interfaces/UploadResponse.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { AITagItem, CMValues, EmbeddedMetadataValues } from "./FileDetails"; -import { FileMetadataResponse } from "./FileMetadata"; -import { FileType } from "./FileType"; - -/** - * Response from uploading a file - * - * @see {@link https://docs.imagekit.io/api-reference/upload-file-api/server-side-file-upload#response-code-and-structure-json} - */ -export interface UploadResponse { - /** - * Unique fileId. Store this fileld in your database, as this will be used to perform update action on this file. - */ - fileId: string; - /** - * The name of the uploaded file. - */ - name: string; - /** - * The URL of the file. - */ - url: string; - /** - * In case of an image, a small thumbnail URL. - */ - thumbnailUrl: string; - /** - * Height of the uploaded image file. Only applicable when file type is image. - */ - height: number; - /** - * Width of the uploaded image file. Only applicable when file type is image. - */ - width: number; - /** - * Size of the uploaded file in bytes. - */ - size: number; - /** - * Type of file. It can either be image or non-image. - */ - fileType: FileType; - /** - * The path of the file uploaded. It includes any folder that you specified while uploading. - */ - filePath: string; - /** - * Array of tags associated with the image. - */ - tags?: string[]; - /** - * Is the file marked as private. It can be either true or false. - */ - isPrivateFile: boolean; - /** - * Value of custom coordinates associated with the image in format x,y,width,height. - */ - customCoordinates: string | null; - /** - * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. - */ - metadata?: FileMetadataResponse; - /* - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. - */ - AITags?: AITagItem[]; - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } - /* - * Consolidated embedded metadata associated with the file. It includes exif, iptc, and xmp data. - */ - embeddedMetadata?: EmbeddedMetadataValues | null; - /* - * A key-value data associated with the asset. Before setting any custom metadata on an asset, you have to create the field using custom metadata fields API. - */ - customMetadata?: CMValues; - /** - * Is the file published or in draft state. It can be either true or false. - */ - isPublished?: boolean -} diff --git a/libs/interfaces/UrlOptions.ts b/libs/interfaces/UrlOptions.ts deleted file mode 100644 index d3dc6ac7..00000000 --- a/libs/interfaces/UrlOptions.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { TransformationPosition } from "."; -import { Transformation } from "./Transformation"; - -export interface UrlOptionsBase { - /** - * An array of objects specifying the transformations to be applied in the URL. - * The transformation name and the value should be specified as a key-value pair in each object. - * @see {@link https://docs.imagekit.io/features/image-transformations/chained-transformations} - */ - transformation?: Array; - /** - * Default value is path that places the transformation string as a path parameter in the URL. - * Can also be specified as query which adds the transformation string as the query parameter tr in the URL. - * If you use src parameter to create the URL, then the transformation string is always added as a query parameter. - */ - transformationPosition?: TransformationPosition; - /** - * These are the other query parameters that you want to add to the final URL. - * These can be any query parameters and not necessarily related to ImageKit. - * Especially useful, if you want to add some versioning parameter to your URLs. - */ - queryParameters?: { [key: string]: string }; - /** - * The base URL to be appended before the path of the image. - * If not specified, the URL Endpoint specified at the time of SDK initialization is used. - */ - urlEndpoint?: string; - /** - * Default is false. If set to true, the SDK generates a signed image URL adding the image signature to the image URL. - * If you are creating URL using src parameter instead of path then do correct urlEndpoint for this to work. - * Otherwise returned URL will have wrong signature. - */ - signed?: boolean; - /** - * Meant to be used along with the signed parameter to specify the time in seconds from now when the URL should expire. - * If specified, the URL contains the expiry timestamp in the URL and the image signature is modified accordingly. - */ - expireSeconds?: number; -} - -export interface UrlOptionsSrc extends UrlOptionsBase { - /** - * Conditional. This is the complete URL of an image already mapped to ImageKit. - * For example, https://ik.imagekit.io/your_imagekit_id/endpoint/path/to/image.jpg. - * Either the path or src parameter need to be specified for URL generation. - */ - src: string; - path?: never; -} - -export interface UrlOptionsPath extends UrlOptionsBase { - /** - * Conditional. This is the path at which the image exists. - * For example, /path/to/image.jpg. Either the path or src parameter need to be specified for URL generation. - */ - path: string; - src?: never; -} - -/** - * Options for generating an URL - * - * @see {@link https://github.com/imagekit-developer/imagekit-nodejs#url-generation} - */ -export type UrlOptions = UrlOptionsSrc | UrlOptionsPath; diff --git a/libs/interfaces/index.ts b/libs/interfaces/index.ts deleted file mode 100644 index 538a897d..00000000 --- a/libs/interfaces/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { ImageKitOptions } from "./ImageKitOptions"; -import { Transformation, TransformationPosition } from "./Transformation"; -import { UploadOptions } from "./UploadOptions"; -import { UploadResponse } from "./UploadResponse"; -import { FileType } from "./FileType"; -import { UrlOptions } from "./UrlOptions"; -import { ListFileOptions, ListFileResponse } from "./ListFile"; -import { CopyFileOptions } from "./CopyFile"; -import { MoveFileOptions } from "./MoveFile"; -import { CreateFolderOptions } from "./CreateFolder"; -import { FileDetailsOptions, FileVersionDetailsOptions, FileObject, FolderObject } from "./FileDetails"; -import { FileMetadataResponse } from "./FileMetadata"; -import { PurgeCacheResponse, PurgeCacheStatusResponse } from "./PurgeCache"; -import { BulkDeleteFilesResponse, BulkDeleteFilesError } from "./BulkDeleteFiles"; -import { CopyFolderOptions, CopyFolderResponse, CopyFolderError } from "./CopyFolder"; -import { MoveFolderOptions, MoveFolderResponse, MoveFolderError } from "./MoveFolder"; -import { DeleteFileVersionOptions, RestoreFileVersionOptions } from "./FileVersion" -import { CreateCustomMetadataFieldOptions, CustomMetadataField, UpdateCustomMetadataFieldOptions, GetCustomMetadataFieldsOptions } from "./CustomMetatadaField" -import { RenameFileOptions, RenameFileResponse } from "./Rename" -import { - WebhookEvent, - WebhookEventVideoTransformationAccepted, - WebhookEventVideoTransformationReady, - WebhookEventVideoTransformationError, - WebhookEventUploadPreTransformationSuccess, - WebhookEventUploadPreTransformationError, - WebhookEventUploadPostTransformationSuccess, - WebhookEventUploadPostTransformationError -} from "./webhookEvent"; - -type FinalUrlOptions = ImageKitOptions & UrlOptions; // actual options used to construct url - -export type { - ImageKitOptions, - Transformation, - TransformationPosition, - UploadOptions, - UploadResponse, - FileType, - UrlOptions, - FinalUrlOptions, - ListFileOptions, - ListFileResponse, - FileDetailsOptions, - FileVersionDetailsOptions, - FileObject, - FolderObject, - FileMetadataResponse, - PurgeCacheResponse, - PurgeCacheStatusResponse, - BulkDeleteFilesResponse, - BulkDeleteFilesError, - CopyFolderResponse, - CopyFolderError, - MoveFolderResponse, - MoveFolderError, - CopyFileOptions, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - CreateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - RenameFileOptions, - RenameFileResponse, - WebhookEvent, - WebhookEventVideoTransformationAccepted, - WebhookEventVideoTransformationReady, - WebhookEventVideoTransformationError, - WebhookEventUploadPostTransformationSuccess, - WebhookEventUploadPostTransformationError, - WebhookEventUploadPreTransformationSuccess, - WebhookEventUploadPreTransformationError -}; -export type { IKCallback } from "./IKCallback"; diff --git a/libs/interfaces/webhookEvent.ts b/libs/interfaces/webhookEvent.ts deleted file mode 100644 index 194afdcd..00000000 --- a/libs/interfaces/webhookEvent.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { UploadResponse } from "./UploadResponse"; - -type Asset = { - url: string; -}; - -type TransformationOptions = { - video_codec: string; - audio_codec: string; - auto_rotate: boolean; - quality: number; - format: string; -}; - -interface WebhookEventBase { - type: string; - id: string; - created_at: string; // Date -} - -/** WebhookEvent for "video.transformation.*" type */ -interface WebhookEventVideoTransformationBase extends WebhookEventBase { - request: { - x_request_id: string; - url: string; - user_agent: string; - }; -} - -export interface WebhookEventVideoTransformationAccepted extends WebhookEventVideoTransformationBase { - type: "video.transformation.accepted"; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - }; - }; -} - -export interface WebhookEventVideoTransformationReady extends WebhookEventVideoTransformationBase { - type: "video.transformation.ready"; - timings: { - donwload_duration: number; - encoding_duration: number; - }; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - output: { - url: string; - video_metadata: { - duration: number; - width: number; - height: number; - bitrate: number; - }; - }; - }; - }; -} - -export interface WebhookEventVideoTransformationError extends WebhookEventVideoTransformationBase { - type: "video.transformation.error"; - data: { - asset: Asset; - transformation: { - type: string; - options: TransformationOptions; - error: { - reason: string; - }; - }; - }; -} - -type TransformationType = "transformation" | "abs" | "gif-to-video" | "thumbnail"; - -interface PreTransformationBase { - id: string; - created_at: string; - request: { - x_request_id: string; - transformation: string; - }; -} - -interface PostTransformationBase { - id: string; - created_at: string; - request: { - x_request_id: string; - transformation: { - type: TransformationType; - value?: string; - protocol?: 'hls' | 'dash'; - }; - }; -} - -export interface WebhookEventUploadPreTransformationSuccess extends PreTransformationBase { - type: "upload.pre-transform.success"; - data: UploadResponse; -} - -export interface WebhookEventUploadPreTransformationError extends PostTransformationBase { - type: "upload.pre-transform.error"; - data: { - name: string; - path: string; - transformation: { - error: { - reason: string; - }; - }; - }; -} - -export interface WebhookEventUploadPostTransformationSuccess extends PostTransformationBase { - type: "upload.post-transform.success"; - data: { - fileId: string; - url: string; - name: string; - }; -} - -export interface WebhookEventUploadPostTransformationError extends PostTransformationBase { - type: "upload.post-transform.error"; - data: { - fileId: string; - url: string; - name: string; - path: string; - transformation: { - error: { - reason: string; - }; - }; - }; -} - -export type WebhookEvent = - | WebhookEventVideoTransformationAccepted - | WebhookEventVideoTransformationReady - | WebhookEventVideoTransformationError - | WebhookEventUploadPreTransformationSuccess - | WebhookEventUploadPreTransformationError - | WebhookEventUploadPostTransformationSuccess - | WebhookEventUploadPostTransformationError - | Object; diff --git a/libs/manage/cache.ts b/libs/manage/cache.ts deleted file mode 100644 index a3bb4ef7..00000000 --- a/libs/manage/cache.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { ImageKitOptions, PurgeCacheResponse, PurgeCacheStatusResponse } from "../interfaces"; - -const purgeCache = function (url: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!url && !url.length) { - respond(true, errorMessages.CACHE_PURGE_URL_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/purge", - method: "POST", - json: { - url: url, - }, - }; - - request(requestOptions, defaultOptions, callback); -}; - -const getPurgeCacheStatus = function ( - requestId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!requestId && !requestId.length) { - respond(true, errorMessages.CACHE_PURGE_STATUS_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/purge/" + requestId, - method: "GET", - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { purgeCache, getPurgeCacheStatus }; diff --git a/libs/manage/custom-metadata-field.ts b/libs/manage/custom-metadata-field.ts deleted file mode 100644 index 752ab080..00000000 --- a/libs/manage/custom-metadata-field.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { - ImageKitOptions, - CreateCustomMetadataFieldOptions, - CustomMetadataField, - UpdateCustomMetadataFieldOptions, - GetCustomMetadataFieldsOptions, -} from "../interfaces"; - -const create = function (createCustomMetadataFieldOptions: CreateCustomMetadataFieldOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - const { name, label, schema } = createCustomMetadataFieldOptions; - if (!name || !name.length) { - respond(true, errorMessages.CMF_NAME_MISSING, callback); - return; - } - - if (!label || !label.length) { - respond(true, errorMessages.CMF_LABEL_MISSING, callback); - return; - } - - if (!schema) { - respond(true, errorMessages.CMF_SCHEMA_MISSING, callback); - return; - } - - if (!schema.type) { - respond(true, errorMessages.CMF_SCHEMA_INVALID, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/customMetadataFields", - method: "POST", - json: { - name, - label, - schema - }, - }; - - request(requestOptions, defaultOptions, callback); -}; - -const list = function ( - getCustomMetadataFieldsOptions: GetCustomMetadataFieldsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { includeDeleted = false } = getCustomMetadataFieldsOptions || {}; - var requestOptions = { - url: "https://api.imagekit.io/v1/customMetadataFields", - method: "GET", - qs: { includeDeleted } - }; - - request(requestOptions, defaultOptions, callback); -}; - -const update = function (fieldId: string, updateCustomMetadataFieldOptions: UpdateCustomMetadataFieldOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!fieldId || typeof fieldId !== "string" || !fieldId.length) { - respond(true, errorMessages.CMF_FIELD_ID_MISSING, callback); - return; - } - - const { label, schema } = updateCustomMetadataFieldOptions; - if (!label && !schema) { - respond(true, errorMessages.CMF_LABEL_SCHEMA_MISSING, callback); - return; - } - - var requestBody: UpdateCustomMetadataFieldOptions = {}; - if (label) requestBody.label = label; - if (schema) requestBody.schema = schema; - - var requestOptions = { - url: `https://api.imagekit.io/v1/customMetadataFields/${fieldId}`, - method: "PATCH", - json: requestBody - }; - - request(requestOptions, defaultOptions, callback); -}; - -const deleteField = function ( - fieldId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fieldId || typeof fieldId !== "string" || !fieldId.length) { - respond(true, errorMessages.CMF_FIELD_ID_MISSING, callback); - return; - } - var requestOptions = { - url: `https://api.imagekit.io/v1/customMetadataFields/${fieldId}`, - method: "DELETE", - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { create, list, update, deleteField }; diff --git a/libs/manage/file.ts b/libs/manage/file.ts deleted file mode 100644 index 5ee18da7..00000000 --- a/libs/manage/file.ts +++ /dev/null @@ -1,699 +0,0 @@ -import { isObject } from 'lodash'; - -/* - Constants -*/ -import errorMessages from "../constants/errorMessages"; - -/* - Utils -*/ -import respond from "../../utils/respond"; -import request from "../../utils/request"; - -/* - Interfaces -*/ -import { IKCallback } from "../interfaces/IKCallback"; -import { - ImageKitOptions, - ListFileOptions, - ListFileResponse, - FileDetailsOptions, - FileVersionDetailsOptions, - FileObject, - FileMetadataResponse, - BulkDeleteFilesResponse, - BulkDeleteFilesError, - CopyFileOptions, - CopyFolderResponse, - MoveFileOptions, - CreateFolderOptions, - CopyFolderOptions, - MoveFolderOptions, - DeleteFileVersionOptions, - RestoreFileVersionOptions, - RenameFileOptions, - RenameFileResponse, -} from "../interfaces"; -import ImageKit from "../.."; - -/* - Delete a file -*/ -const deleteFile = function (fileId: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId, - method: "DELETE" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Delete a file version -*/ -const deleteFileVersion = function (deleteFileVersionOptions: DeleteFileVersionOptions, defaultOptions: ImageKitOptions, callback?: IKCallback) { - const { fileId, versionId } = deleteFileVersionOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}`, - method: "DELETE" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Restore a file version as the current version -*/ -const restoreFileVersion = function ( - restoreFileVersionOptions: RestoreFileVersionOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback) { - const { fileId, versionId } = restoreFileVersionOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}/restore`, - method: "PUT" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Get Metadata of a file -*/ -const getMetadata = function ( - fileIdOrURL: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileIdOrURL || fileIdOrURL.trim() == "") { - respond(true, errorMessages.FILE_ID_OR_URL_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileIdOrURL + "/metadata", - method: "GET" - }; - - // In case of URL change the endopint - if (fileIdOrURL.indexOf("http") === 0) { - requestOptions = { - url: `https://api.imagekit.io/v1/metadata?url=${fileIdOrURL}`, - method: "GET" - }; - } - - request(requestOptions, defaultOptions, callback); -}; - -/* - Get Details of a file -*/ -const getDetails = function ( - fileId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId + "/details", - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Get Details of a file version -*/ -const getFileVersionDetails = function ( - fileDetailsOptions: FileVersionDetailsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { fileId, versionId } = fileDetailsOptions || {}; - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!versionId) { - respond(true, errorMessages.FILE_VERSION_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions/${versionId}`, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Update file details -*/ -const updateDetails = function ( - fileId: string, - updateData: FileDetailsOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - if (!isObject(updateData)) { - respond(true, errorMessages.UPDATE_DATA_MISSING, callback); - return; - } - - var data = {}; - data = { - tags: updateData.tags, - customCoordinates: updateData.customCoordinates, - extensions: updateData.extensions, - webhookUrl: updateData.webhookUrl, - customMetadata: updateData.customMetadata, - }; - - if (updateData.publish) - data = { - ...data, - publish: updateData.publish, - }; - - var requestOptions = { - url: "https://api.imagekit.io/v1/files/" + fileId + "/details", - method: "PATCH", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - List files -*/ -const listFiles = function ( - listOptions: ListFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (listOptions && !isObject(listOptions)) { - respond(true, errorMessages.INVALID_LIST_OPTIONS, callback); - return; - } - - if (listOptions && listOptions.tags && Array.isArray(listOptions.tags) && listOptions.tags.length) { - listOptions.tags = listOptions.tags.join(","); - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/`, - method: "GET", - qs: listOptions || {} - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Get all versions of an asset -*/ -const getFilesVersions = function ( - fileId: string, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if (!fileId) { - respond(true, errorMessages.FILE_ID_MISSING, callback); - return; - } - - var requestOptions = { - url: `https://api.imagekit.io/v1/files/${fileId}/versions`, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Bulk Delete By FileIds -*/ -const bulkDeleteFiles = function ( - fileIdArray: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - const data = { - fileIds: fileIdArray, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/batch/deleteByFileIds", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Add tags in bulk -*/ -const bulkAddTags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - tags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/addTags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Remove tags in bulk -*/ -const bulkRemoveTags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - tags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/removeTags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - - -/* - Remove AI tags in bulk -*/ -const bulkRemoveAITags = function ( - fileIdArray: string[], - tags: string[], - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - if ( - !Array.isArray(fileIdArray) || - fileIdArray.length === 0 || - fileIdArray.filter((fileId) => typeof fileId !== "string").length > 0 - ) { - respond(true, errorMessages.INVALID_FILEIDS_VALUE, callback); - return; - } - - if (!Array.isArray(tags) || tags.length === 0 || tags.filter((tag) => typeof tag !== "string").length > 0) { - respond(true, errorMessages.BULK_ADD_TAGS_INVALID, callback); - return; - } - - const data = { - fileIds: fileIdArray, - AITags: tags, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/removeAITags", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Copy file -*/ -const copyFile = function ( - copyFileOptions: CopyFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFilePath, destinationPath, includeFileVersions = false } = copyFileOptions; - - if (typeof sourceFilePath !== "string" || sourceFilePath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FILE_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - if (typeof includeFileVersions !== "boolean") { - respond(true, errorMessages.INVALID_INCLUDE_VERSION, callback); - return; - } - - const data = { - sourceFilePath, - destinationPath, - includeFileVersions - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/copy", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Move file -*/ -const moveFile = function ( - moveFileOptions: MoveFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFilePath, destinationPath } = moveFileOptions; - if (typeof sourceFilePath !== "string" || sourceFilePath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FILE_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - const data = { - sourceFilePath, - destinationPath - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/move", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Rename file -*/ -const renameFile = function ( - renameFileOptions: RenameFileOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { filePath, newFileName, purgeCache = false } = renameFileOptions; - if (typeof filePath !== "string" || filePath.length === 0) { - respond(true, errorMessages.INVALID_FILE_PATH, callback); - return; - } - - if (typeof newFileName !== "string" || newFileName.length === 0) { - respond(true, errorMessages.INVALID_NEW_FILE_NAME, callback); - return; - } - - if (typeof purgeCache !== "boolean") { - respond(true, errorMessages.INVALID_PURGE_CACHE, callback); - return; - } - - const data = { - filePath, - newFileName, - purgeCache - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/files/rename", - method: "PUT", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Copy Folder -*/ -const copyFolder = function ( - copyFolderOptions: CopyFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFolderPath, destinationPath, includeFileVersions = false } = copyFolderOptions; - if (typeof sourceFolderPath !== "string" || sourceFolderPath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FOLDER_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - if (typeof includeFileVersions !== "boolean") { - respond(true, errorMessages.INVALID_INCLUDE_VERSION, callback); - return; - } - - const data = { - sourceFolderPath, - destinationPath, - includeFileVersions - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/copyFolder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Move Folder -*/ -const moveFolder = function ( - moveFolderOptions: MoveFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { sourceFolderPath, destinationPath } = moveFolderOptions; - - if (typeof sourceFolderPath !== "string" || sourceFolderPath.length === 0) { - respond(true, errorMessages.INVALID_SOURCE_FOLDER_PATH, callback); - return; - } - - if (typeof destinationPath !== "string" || destinationPath.length === 0) { - respond(true, errorMessages.INVALID_DESTINATION_FOLDER_PATH, callback); - return; - } - - const data = { - sourceFolderPath, - destinationPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/moveFolder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Create folder -*/ -const createFolder = function ( - createFolderOptions: CreateFolderOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - const { folderName, parentFolderPath } = createFolderOptions; - if (typeof folderName !== "string" || folderName.length === 0) { - respond(true, errorMessages.INVALID_FOLDER_NAME, callback); - return; - } - - if (typeof parentFolderPath !== "string" || parentFolderPath.length === 0) { - respond(true, errorMessages.INVALID_PARENT_FOLDER_PATH, callback); - return; - } - - const data = { - folderName: folderName, - parentFolderPath: parentFolderPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/folder", - method: "POST", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Delete folder -*/ -const deleteFolder = function (folderPath: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (typeof folderPath !== "string" || folderPath.length === 0) { - respond(true, errorMessages.INVALID_FOLDER_PATH, callback); - return; - } - - const data = { - folderPath: folderPath, - }; - - const requestOptions = { - url: "https://api.imagekit.io/v1/folder", - method: "DELETE", - json: data, - }; - - request(requestOptions, defaultOptions, callback); -}; - -/* - Bulk job status -*/ -const getBulkJobStatus = function (jobId: string, defaultOptions: ImageKitOptions, callback?: IKCallback) { - if (!jobId) { - respond(true, errorMessages.JOB_ID_MISSING, callback); - return; - } - - const requestOptions = { - url: "https://api.imagekit.io/v1/bulkJobs/" + jobId, - method: "GET" - }; - - request(requestOptions, defaultOptions, callback); -}; - -export default { - deleteFile, - getMetadata, - getDetails, - getFileVersionDetails, - updateDetails, - listFiles, - getFilesVersions, - bulkDeleteFiles, - deleteFileVersion, - restoreFileVersion, - bulkAddTags, - bulkRemoveTags, - bulkRemoveAITags, - copyFile, - moveFile, - renameFile, - copyFolder, - moveFolder, - createFolder, - deleteFolder, - getBulkJobStatus, -}; diff --git a/libs/manage/index.ts b/libs/manage/index.ts deleted file mode 100644 index 72f4ac34..00000000 --- a/libs/manage/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import cache from "./cache"; -import file from "./file"; -import customMetadataField from "./custom-metadata-field"; - -export default { - listFiles: file.listFiles, - getFileDetails: file.getDetails, - getFileVersions: file.getFilesVersions, - getFileVersionDetails: file.getFileVersionDetails, - updateFileDetails: file.updateDetails, - getFileMetadata: file.getMetadata, - deleteFile: file.deleteFile, - bulkDeleteFiles: file.bulkDeleteFiles, - deleteFileVersion: file.deleteFileVersion, - restoreFileVersion: file.restoreFileVersion, - bulkAddTags: file.bulkAddTags, - bulkRemoveTags: file.bulkRemoveTags, - bulkRemoveAITags: file.bulkRemoveAITags, - copyFile: file.copyFile, - moveFile: file.moveFile, - renameFile: file.renameFile, - copyFolder: file.copyFolder, - moveFolder: file.moveFolder, - createFolder: file.createFolder, - deleteFolder: file.deleteFolder, - getBulkJobStatus: file.getBulkJobStatus, - purgeCache: cache.purgeCache, - getPurgeCacheStatus: cache.getPurgeCacheStatus, - createCustomMetadataField: customMetadataField.create, - getCustomMetadataFields: customMetadataField.list, - updateCustomMetadataField: customMetadataField.update, - deleteCustomMetadataField: customMetadataField.deleteField, -}; diff --git a/libs/signature/index.ts b/libs/signature/index.ts deleted file mode 100644 index 8d17acec..00000000 --- a/libs/signature/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - Helper Modules -*/ -import { v4 as uuid } from "uuid"; -import crypto from "crypto"; -import { ImageKitOptions } from "../interfaces"; -var DEFAULT_TIME_DIFF = 60 * 30; - -const getAuthenticationParameters = function (token?: string, expire?: number, defaultOptions?: ImageKitOptions) { - var defaultExpire = parseInt(String(new Date().getTime() / 1000), 10) + DEFAULT_TIME_DIFF; - var authParameters = { - token: token || "", - expire: expire || 0, - signature: "", - }; - - if (!defaultOptions || !defaultOptions.privateKey) return authParameters; - - token = token || uuid(); - expire = expire || defaultExpire; - var signature = crypto - .createHmac("sha1", defaultOptions.privateKey) - .update(token + expire) - .digest("hex"); - - authParameters.token = token; - authParameters.expire = expire; - authParameters.signature = signature; - - return authParameters; -}; - -export default { getAuthenticationParameters }; diff --git a/libs/upload/index.ts b/libs/upload/index.ts deleted file mode 100644 index 7d1a8577..00000000 --- a/libs/upload/index.ts +++ /dev/null @@ -1,110 +0,0 @@ -import _ from "lodash"; -import errorMessages from "../constants/errorMessages"; -import respond from "../../utils/respond"; -import request from "../../utils/request"; -import { IKCallback } from "../interfaces/IKCallback"; -import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; -import FormData from "form-data"; - -type Modify = Omit & R; -type FormDataOptions = Modify< - UploadOptions, - { - file: string | Buffer | object; - useUniqueFileName: string; - isPrivateFile: string; - extensions?: string; - webhookUrl?: string; - overwriteFile?: string; - overwriteAITags?: string; - overwriteTags?: string; - overwriteCustomMetadata?: string; - customMetadata?: string; - } ->; - -export default function ( - uploadOptions: UploadOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -): void | Promise { - if (!_.isObject(uploadOptions)) { - respond(true, errorMessages.MISSING_UPLOAD_DATA, callback); - return; - } - - if (!uploadOptions.file) { - respond(true, errorMessages.MISSING_UPLOAD_FILE_PARAMETER, callback); - return; - } - - if (!uploadOptions.fileName) { - respond(true, errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER, callback); - return; - } - - if (uploadOptions.transformation) { - if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { - respond(true, errorMessages.INVALID_TRANSFORMATION, callback); - return; - } - if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { - respond(true, errorMessages.INVALID_PRE_TRANSFORMATION, callback); - return; - } - if (Object.keys(uploadOptions.transformation).includes("post")) { - if (Array.isArray(uploadOptions.transformation.post)) { - for (let transformation of uploadOptions.transformation.post) { - if (transformation.type === "abs" && !(transformation.protocol || transformation.value)) { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } else if (transformation.type === "transformation" && !transformation.value) { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } - } - } else { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; - } - } - } - - var formData = {} as FormDataOptions; - - const form = new FormData(); - - let key: keyof typeof uploadOptions; - for (key in uploadOptions) { - if (key) { - if (key == "file" && typeof uploadOptions.file != "string") { - // form.append('file', uploadOptions.file); - form.append('file', uploadOptions.file, String(uploadOptions.fileName)); - } else if (key == "tags" && Array.isArray(uploadOptions.tags)) { - form.append('tags', uploadOptions.tags.join(",")); - } else if (key == "responseFields" && Array.isArray(uploadOptions.responseFields)) { - form.append('responseFields', uploadOptions.responseFields.join(",")); - } else if (key == "extensions" && Array.isArray(uploadOptions.extensions)) { - form.append('extensions', JSON.stringify(uploadOptions.extensions)); - } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && - !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { - form.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); - } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && - uploadOptions.transformation !== null) { - form.append(key, JSON.stringify(uploadOptions.transformation)); - } else if (key === "checks" && uploadOptions.checks) { - form.append(key, uploadOptions.checks); - } else { - form.append(key, String(uploadOptions[key])); - } - } - } - - var requestOptions = { - url: defaultOptions.uploadEndpoint || "https://upload.imagekit.io/api/v1/files/upload", - method: "POST", - formData: form - }; - - request(requestOptions, defaultOptions, callback); -} diff --git a/libs/url/builder.ts b/libs/url/builder.ts deleted file mode 100644 index 87319978..00000000 --- a/libs/url/builder.ts +++ /dev/null @@ -1,179 +0,0 @@ -/* - Helper Modules -*/ -import { URLSearchParams, URL } from "url"; -import path from "path"; -import crypto from "crypto"; - -/* - Utils -*/ -import transformationUtils from "../../utils/transformation"; -import urlFormatter from "../../utils/urlFormatter"; - -/* - Interfaces -*/ -import { FinalUrlOptions, Transformation } from "../interfaces"; - -/* - Variables -*/ -const TRANSFORMATION_PARAMETER: string = "tr"; -const SIGNATURE_PARAMETER: string = "ik-s"; -const TIMESTAMP_PARAMETER: string = "ik-t"; -const DEFAULT_TIMESTAMP: string = "9999999999"; - -//used to check if special char is present in string (you'll need to encode it to utf-8 if it does) -const hasMoreThanAscii = (str: string) => { - return str.split('').some((char) => char.charCodeAt(0) > 127); -} - -const customEncodeURI = (str: string) => { - return str.includes("?") ? `${encodeURI(str.split("?")[0])}?${str.split("?")[1]}` : encodeURI(str); -}; - -export const encodeStringIfRequired = (str: string) => { - return hasMoreThanAscii(str) ? customEncodeURI(str) : str; -} - -const buildURL = function (opts: FinalUrlOptions): string { - var isSrcParameterUsedForURL: boolean = false; - - var urlObject: URL; - - if (opts.path) { - urlObject = new URL(opts.urlEndpoint) - } else if (opts.src) { - isSrcParameterUsedForURL = true; - urlObject = new URL(opts.src) - } else { - return ""; - } - - - var queryParameters = new URLSearchParams(urlObject.search || ""); - for (var i in opts.queryParameters) { - queryParameters.set(i, opts.queryParameters[i]); - } - - //Create Transformation String - var transformationString = constructTransformationString(opts.transformation); - if (transformationString) { - //force that if src parameter is being used for URL construction then the transformation - //string should be added only as a query parameter - if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { - queryParameters.set(TRANSFORMATION_PARAMETER, transformationString); - urlObject.pathname= `${urlObject.pathname}${opts.path||''}`; - } else { - urlObject.pathname = path.posix.join( - urlObject.pathname, - [TRANSFORMATION_PARAMETER, transformationString].join(transformationUtils.getChainTransformDelimiter()), - opts.path || '', - ); - } - } - else{ - urlObject.pathname= `${urlObject.pathname}${opts.path||''}`; - } - - urlObject.host = urlFormatter.removeTrailingSlash(urlObject.host); - urlObject.pathname = urlFormatter.addLeadingSlash(urlObject.pathname); - urlObject.search = queryParameters.toString(); - - /* - Signature String and Timestamp - If the url is constructed using src parameter instead of path then we still replace the urlEndpoint we have - But the user is responsible for passing correct urlEndpoint value - - Signature generation logic, let's assume: - urlEndpoint value = https://ik.imagekit.io/your_imagekit_id - expiryTimestamp 9999999999 - 1. Let the final URL construct e.g. https://ik.imagekit.io/your_imagekit_id/tr:w-400:rotate-91/sample/testing-file.jpg?param1=123 - 2. Now remove urlEndpoint from it i.e tr:w-400:rotate-91/sample/testing-file.jpg?param1=123 - 3. Append expiryTimestamp to above string and calcualte signature of this string i.e "tr:w-400:rotate-91/sample/testing-file.jpg?param1=1239999999999" - */ - var expiryTimestamp; - if (opts.signed === true) { - if (opts.expireSeconds) { - expiryTimestamp = getSignatureTimestamp(opts.expireSeconds); - } else { - expiryTimestamp = DEFAULT_TIMESTAMP; - } - - var intermediateURL = urlObject.href; - - var urlSignature = getSignature({ - privateKey: opts.privateKey, - url: intermediateURL, - urlEndpoint: opts.urlEndpoint, - expiryTimestamp: expiryTimestamp, - }); - - if (expiryTimestamp && expiryTimestamp != DEFAULT_TIMESTAMP) { - queryParameters.set(TIMESTAMP_PARAMETER, expiryTimestamp); - } - queryParameters.set(SIGNATURE_PARAMETER, urlSignature); - urlObject.search = queryParameters.toString(); - } - return urlObject.href; -}; - -function constructTransformationString(inputTransformation: Array | undefined) { - - const transformation = inputTransformation as Array<{ [key: string]: string | boolean | number }> | undefined; - if (!Array.isArray(transformation)) { - return ""; - } - - var parsedTransforms = []; - for (var i = 0, l = transformation.length; i < l; i++) { - var parsedTransformStep = []; - for (var key in transformation[i]) { - if(transformation[i][key] === undefined || transformation[i][key] === null ) - continue; - let transformKey = transformationUtils.getTransformKey(key); - if (!transformKey) { - transformKey = key; - } - - if (transformation[i][key] === "-") { - parsedTransformStep.push(transformKey); - } else if (key === "raw") { - parsedTransformStep.push(transformation[i][key]); - } else { - var value = String(transformation[i][key]); - if (transformKey === "di") { - value = urlFormatter.removeTrailingSlash(urlFormatter.removeLeadingSlash(value)); - if (value) value = value.replace(/\//g, "@@"); - } - parsedTransformStep.push([transformKey, value].join(transformationUtils.getTransformKeyValueDelimiter())); - } - } - parsedTransforms.push(parsedTransformStep.join(transformationUtils.getTransformDelimiter())); - } - - return parsedTransforms.join(transformationUtils.getChainTransformDelimiter()); -} - -function getSignatureTimestamp(seconds: number): string { - if (!seconds) return DEFAULT_TIMESTAMP; - - var sec = parseInt(String(seconds), 10); - if (!sec) return DEFAULT_TIMESTAMP; - - var currentTimestamp = parseInt(String(new Date().getTime() / 1000), 10); - return String(currentTimestamp + sec); -} - -export function getSignature(opts: any) { - if (!opts.privateKey || !opts.url || !opts.urlEndpoint) return ""; - var stringToSign = opts.url.replace(urlFormatter.addTrailingSlash(opts.urlEndpoint), "") + opts.expiryTimestamp; - stringToSign = encodeStringIfRequired(stringToSign); - return crypto.createHmac("sha1", opts.privateKey).update(stringToSign).digest("hex"); -} - -export default { - buildURL, - getSignature, -}; diff --git a/libs/url/index.ts b/libs/url/index.ts deleted file mode 100644 index 35cd2d50..00000000 --- a/libs/url/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - Helper Modules -*/ -import _ from "lodash"; -/* - Interfaces -*/ -import { UrlOptions, ImageKitOptions, FinalUrlOptions } from "../interfaces"; -/* - URL builder -*/ -import builder from "./builder"; - -export default function (urlOpts: UrlOptions, defaultOptions: ImageKitOptions): string { - var opts: FinalUrlOptions = _.extend({}, defaultOptions, urlOpts); - - return builder.buildURL(opts); -} diff --git a/package.json b/package.json index 40f220ed..c2399b07 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,65 @@ { - "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" - }, - "bugs": { - "url": "https://github.com/imagekit-developer/imagekit-nodejs/issues" - }, - "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" - }, - "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" - } + "name": "imagekit", + "version": "6.0.1", + "private": false, + "repository": "github:imagekit-developer/imagekit-nodejs", + "type": "commonjs", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.ts", + "exports": { + ".": { + "types": "./dist/cjs/index.d.ts", + "import": { + "types": "./dist/esm/index.d.mts", + "default": "./dist/esm/index.mjs" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "default": "./dist/cjs/index.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "dist", + "reference.md", + "README.md", + "LICENSE" + ], + "scripts": { + "format": "prettier . --write --ignore-unknown", + "build": "yarn build:cjs && yarn build:esm", + "build:cjs": "tsc --project ./tsconfig.cjs.json", + "build:esm": "tsc --project ./tsconfig.esm.json && node scripts/rename-to-esm-files.js dist/esm", + "test": "jest --config jest.config.mjs", + "test:unit": "jest --selectProjects unit", + "test:browser": "jest --selectProjects browser", + "test:wire": "jest --selectProjects wire" + }, + "devDependencies": { + "webpack": "^5.97.1", + "ts-loader": "^9.5.1", + "jest": "^29.7.0", + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.14", + "ts-jest": "^29.3.4", + "jest-environment-jsdom": "^29.7.0", + "msw": "^2.8.4", + "@types/node": "^18.19.70", + "prettier": "^3.4.2", + "typescript": "~5.7.2" + }, + "browser": { + "fs": false, + "os": false, + "path": false, + "stream": false + }, + "packageManager": "yarn@1.22.22", + "engines": { + "node": ">=18.0.0" + }, + "sideEffects": false } diff --git a/reference.md b/reference.md new file mode 100644 index 00000000..9ca8d53f --- /dev/null +++ b/reference.md @@ -0,0 +1,2874 @@ +# Reference + +## Files + +
client.files.upload({ ...params }) -> ImageKit.Upload +
+
+ +#### 📝 Description + +
+
+ +
+
+ +ImageKit.io allows you to upload files directly from both the server and client sides. For server-side uploads, private API key authentication is used. For client-side uploads, generate a one-time `token`, `signature`, and `expiration` 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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.upload({ + file: fs.createReadStream("/path/to/your/file"), + fileName: "fileName", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FileUploadV1` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.get(fileId) -> ImageKit.FileDetails +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API returns an object with details or attributes about the current version of the file. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.get("fileId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in the list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.update(fileId, { ...params }) -> ImageKit.FilesUpdateResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API updates the details or attributes of the current version of the file. You can update `tags`, `customCoordinates`, `customMetadata`, publication status, remove existing `AITags` and apply extensions using this API. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.update("fileId", { + removeAITags: ["car", "vehicle", "motorsports"], + webhookUrl: "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + 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", + }, + ], + tags: ["tag1", "tag2"], + customCoordinates: "10,10,100,100", + customMetadata: { + brand: "Nike", + color: "red", + }, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**request:** `ImageKit.FilesUpdateRequest` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.delete(fileId) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.delete("fileId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.copy({ ...params }) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.copy({ + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/copy/into/", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FilesCopyRequest` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.move({ ...params }) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.move({ + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/move/into/", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FilesMoveRequest` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.rename({ ...params }) -> ImageKit.FilesRenameResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.rename({ + filePath: "/path/to/file.jpg", + newFileName: "newFileName.jpg", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FilesRenameRequest` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +## CustomMetadataFields + +
client.customMetadataFields.list({ ...params }) -> ImageKit.CustomMetadataField[] +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.customMetadataFields.list(); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.CustomMetadataFieldsListRequest` + +
+
+ +
+
+ +**requestOptions:** `CustomMetadataFields.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.customMetadataFields.create({ ...params }) -> ImageKit.CustomMetadataField +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.customMetadataFields.create({ + name: "price", + label: "price", + schema: { + type: "Number", + minValue: 1000, + maxValue: 3000, + }, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.CustomMetadataFieldsCreateRequest` + +
+
+ +
+
+ +**requestOptions:** `CustomMetadataFields.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.customMetadataFields.delete(id) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.customMetadataFields.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — Should be a valid custom metadata field id. + +
+
+ +
+
+ +**requestOptions:** `CustomMetadataFields.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.customMetadataFields.update(id, { ...params }) -> ImageKit.CustomMetadataField +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API updates the label or schema of an existing custom metadata field. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.customMetadataFields.update("id", { + label: "price", + schema: { + minValue: 1000, + maxValue: 3000, + }, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — Should be a valid custom metadata field id. + +
+
+ +
+
+ +**request:** `ImageKit.CustomMetadataFieldsUpdateRequest` + +
+
+ +
+
+ +**requestOptions:** `CustomMetadataFields.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Assets + +
client.assets.list({ ...params }) -> ImageKit.AssetsListResponseItem[] +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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`. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.assets.list(); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.AssetsListRequest` + +
+
+ +
+
+ +**requestOptions:** `Assets.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Folders + +
client.folders.create({ ...params }) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.create({ + folderName: "summer", + parentFolderPath: "/product/images/", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FoldersCreateRequest` + +
+
+ +
+
+ +**requestOptions:** `Folders.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.folders.delete({ ...params }) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This will delete a folder and all its contents permanently. The API returns an empty response. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.delete({ + folderPath: "/folder/to/delete/", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FoldersDeleteRequest` + +
+
+ +
+
+ +**requestOptions:** `Folders.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.folders.copy({ ...params }) -> ImageKit.FoldersCopyResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.copy({ + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FoldersCopyRequest` + +
+
+ +
+
+ +**requestOptions:** `Folders.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.folders.move({ ...params }) -> ImageKit.FoldersMoveResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.move({ + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FoldersMoveRequest` + +
+
+ +
+
+ +**requestOptions:** `Folders.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.folders.rename({ ...params }) -> ImageKit.FoldersRenameResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.rename({ + folderPath: "/path/of/folder", + newFolderName: "new-folder-name", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.FoldersRenameRequest` + +
+
+ +
+
+ +**requestOptions:** `Folders.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Accounts Usage + +
client.accounts.usage.get({ ...params }) -> ImageKit.UsageGetResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.usage.get({ + startDate: "startDate", + endDate: "endDate", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.accounts.UsageGetRequest` + +
+
+ +
+
+ +**requestOptions:** `Usage.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Accounts Origins + +
client.accounts.origins.list() -> ImageKit.OriginResponseArray +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Returns an array of all configured origins for the current account. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.origins.list(); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `Origins.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.origins.create({ ...params }) -> ImageKit.OriginResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Creates a new origin and returns the origin object. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.origins.create({ + type: "S3", + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.OriginSchema` + +
+
+ +
+
+ +**requestOptions:** `Origins.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.origins.get(id) -> ImageKit.OriginResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Retrieves the origin identified by `id`. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.origins.get("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + +
+
+ +
+
+ +**requestOptions:** `Origins.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.origins.update(id, { ...params }) -> ImageKit.OriginResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Updates the origin identified by `id` and returns the updated origin object. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.origins.update("id", { + type: "S3", + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + +
+
+ +
+
+ +**request:** `ImageKit.OriginSchema` + +
+
+ +
+
+ +**requestOptions:** `Origins.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.origins.delete(id) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.origins.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + +
+
+ +
+
+ +**requestOptions:** `Origins.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Accounts UrlEndpoints + +
client.accounts.urlEndpoints.list() -> ImageKit.UrlEndpointResponseArray +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.urlEndpoints.list(); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `UrlEndpoints.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.urlEndpoints.create({ ...params }) -> ImageKit.UrlEndpointResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Creates a new URL‑endpoint and returns the resulting object. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.urlEndpoints.create({ + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.UrlEndpointSchema` + +
+
+ +
+
+ +**requestOptions:** `UrlEndpoints.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.urlEndpoints.get(id) -> ImageKit.UrlEndpointResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Retrieves the URL‑endpoint identified by `id`. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.urlEndpoints.get("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — 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`. + +
+
+ +
+
+ +**requestOptions:** `UrlEndpoints.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.urlEndpoints.update(id, { ...params }) -> ImageKit.UrlEndpointResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**Note:** This API is currently in beta. +Updates the URL‑endpoint identified by `id` and returns the updated object. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.urlEndpoints.update("id", { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — 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`. + +
+
+ +
+
+ +**request:** `ImageKit.UrlEndpointSchema` + +
+
+ +
+
+ +**requestOptions:** `UrlEndpoints.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.accounts.urlEndpoints.delete(id) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.accounts.urlEndpoints.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — 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`. + +
+
+ +
+
+ +**requestOptions:** `UrlEndpoints.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Beta V2 Files + +
client.beta.v2.files.upload({ ...params }) -> ImageKit.Upload +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.beta.v2.files.upload({ + file: fs.createReadStream("/path/to/your/file"), + fileName: "fileName", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.beta.v2.FileUploadV2` + +
+
+ +
+
+ +**requestOptions:** `Files.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Cache Invalidation + +
client.cache.invalidation.create({ ...params }) -> ImageKit.InvalidationCreateResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.cache.invalidation.create({ + url: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.cache.InvalidationCreateRequest` + +
+
+ +
+
+ +**requestOptions:** `Invalidation.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.cache.invalidation.get(requestId) -> ImageKit.InvalidationGetResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API returns the status of a purge cache request. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.cache.invalidation.get("requestId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestId:** `string` — Should be a valid requestId. + +
+
+ +
+
+ +**requestOptions:** `Invalidation.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Files Bulk + +
client.files.bulk.delete({ ...params }) -> ImageKit.BulkDeleteResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.bulk.delete({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.files.BulkDeleteRequest` + +
+
+ +
+
+ +**requestOptions:** `Bulk.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.bulk.addTags({ ...params }) -> ImageKit.BulkAddTagsResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API adds tags to multiple files in bulk. A maximum of 50 files can be specified at a time. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.bulk.addTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.files.BulkAddTagsRequest` + +
+
+ +
+
+ +**requestOptions:** `Bulk.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.bulk.removeTags({ ...params }) -> ImageKit.BulkRemoveTagsResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API removes tags from multiple files in bulk. A maximum of 50 files can be specified at a time. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.bulk.removeTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.files.BulkRemoveTagsRequest` + +
+
+ +
+
+ +**requestOptions:** `Bulk.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.bulk.removeAiTags({ ...params }) -> ImageKit.BulkRemoveAiTagsResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API removes AITags from multiple files in bulk. A maximum of 50 files can be specified at a time. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.bulk.removeAiTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + AITags: ["t-shirt", "round-neck", "sale2019"], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.files.BulkRemoveAiTagsRequest` + +
+
+ +
+
+ +**requestOptions:** `Bulk.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Files Versions + +
client.files.versions.list(fileId) -> ImageKit.FileDetails[] +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API returns details of all versions of a file. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.versions.list("fileId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Versions.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.versions.get(fileId, versionId) -> ImageKit.FileDetails +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API returns an object with details or attributes of a file version. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.versions.get("fileId", "versionId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**versionId:** `string` — The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Versions.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.versions.delete(fileId, versionId) -> Record +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.versions.delete("fileId", "versionId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**versionId:** `string` — The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Versions.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.versions.restore(fileId, versionId) -> ImageKit.FileDetails +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API restores a file version as the current file version. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.versions.restore("fileId", "versionId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**versionId:** `string` — The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Versions.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Files Metadata + +
client.files.metadata.get(fileId) -> ImageKit.Metadata +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.metadata.get("fileId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**fileId:** `string` — The unique `fileId` of the uploaded file. `fileId` is returned in the list and search assets API and upload API. + +
+
+ +
+
+ +**requestOptions:** `Metadata.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.files.metadata.getFromUrl({ ...params }) -> ImageKit.Metadata +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL using this API. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.files.metadata.getFromUrl({ + url: "url", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `ImageKit.files.MetadataGetFromUrlRequest` + +
+
+ +
+
+ +**requestOptions:** `Metadata.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Folders Job + +
client.folders.job.get(jobId) -> ImageKit.JobGetResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This API returns the status of a bulk job like copy and move folder operations. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.folders.job.get("jobId"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**jobId:** `string` — The `jobId` is returned in the response of bulk job API e.g. copy folder or move folder API. + +
+
+ +
+
+ +**requestOptions:** `Job.RequestOptions` + +
+
+
+
+ +
+
+
diff --git a/sample/README.md b/sample/README.md deleted file mode 100644 index bb0fdc28..00000000 --- a/sample/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Running the sample application - -### Step 1: Install dependencies -```bash -npm install -``` - -### Step 2: Enter account details -Open `index.js` and fill the account details: -```js -const CONFIG_OPTIONS = { - publicKey: "your_public_api_key", - privateKey: "your_private_api_key", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/", -}; -``` - -### Step 3: -Run the `index.js` \ No newline at end of file diff --git a/sample/index.js b/sample/index.js deleted file mode 100644 index 032f2d96..00000000 --- a/sample/index.js +++ /dev/null @@ -1,292 +0,0 @@ -const ImageKit = require("imagekit"); -const fs = require("fs"); -const path = require("path"); - -const CONFIG_OPTIONS = { - publicKey: "your_public_api_key", - privateKey: "your_private_api_key", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/", -}; - -const FILE_PATH = path.resolve(__dirname, "./test_image.jpg"), - FILE_NAME = "test_image", - IMG_URL = - "https://images.pexels.com/photos/247676/pexels-photo-247676.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260"; - -const sampleApp = async () => { - try { - const imagekit = new ImageKit(CONFIG_OPTIONS); - - // Uploading images through binary - let i = 0; - while (i < 8) { - const response = await uploadLocalFile(imagekit, FILE_PATH, `${FILE_NAME}_bin_${i + 1}`); - console.log(`Binary upload response # ${i + 1}:`, JSON.stringify(response, undefined, 2), "\n"); - i++; - } - - // Uploading images with base64 - const uploadResponse_base64 = await uploadFileBase64(imagekit, FILE_PATH, `${FILE_NAME}_base64`); - console.log(`Base64 upload response:`, JSON.stringify(uploadResponse_base64, undefined, 2), "\n"); - - // Uploading images with buffer - const uploadResponse_buffer = await uploadFileBuffer(imagekit, FILE_PATH, `${FILE_NAME}_buffer`); - console.log(`Buffer upload response:`, JSON.stringify(uploadResponse_buffer, undefined, 2), "\n"); - - // Uploading images with URL - const uploadResponse_url = await uploadFileURL(imagekit, IMG_URL, `${FILE_NAME}_url`); - console.log(`URL upload response:`, JSON.stringify(uploadResponse_url, undefined, 2), "\n"); - - // Listing Files - const filesList = await listFiles(imagekit, 12, 0); - console.log("List of first 10 files: ", JSON.stringify(filesList, undefined, 2), "\n"); - - // Generating URLs - const imageURL = imagekit.url({ - path: filesList[0].filePath, - transformation: [ - { - height: 300, - width: 400, - }, - ], - }); - console.log("Url for first image transformed with height: 300, width: 400: ", imageURL, "\n"); - - var signedUrl = imagekit.url({ - path: filesList[0].filePath, - signed: true, - transformation: [ - { - height: 300, - width: 400, - }, - ], - }); - console.log("Signed Url for first image transformed with height: 300, width: 400: ", signedUrl, "\n"); - - // Get File Details - const fileDetails_1 = await getFileDetails(imagekit, filesList[0].fileId); - console.log("File Details fetched: ", JSON.stringify(fileDetails_1, undefined, 2), "\n"); - - // Get File Metadata - const fileMetadata_1 = await getFileMetadata(imagekit, filesList[0].fileId); - const fileMetadata_2 = await getFileMetadata(imagekit, filesList[1].fileId); - console.log("File metadata fetched: ", JSON.stringify(fileMetadata_1, undefined, 2), "\n"); - - // Update File Details - const fileUpdateResponse = await updateFileDetails( - imagekit, - filesList[0].fileId, - ["buildings", "day"], - "10,10,100,100", - //Uncomment to send extensions parameter - // [ - // { - // name: "google-auto-tagging", - // maxTags: 5, - // minConfidence: 95 - // } - // ] - ); - console.log("File Update Response: ", JSON.stringify(fileUpdateResponse, undefined, 2), "\n"); - - // pHash Distance - console.log(fileMetadata_1.pHash, fileMetadata_2.pHash); - const pHashDistance = imagekit.pHashDistance(fileMetadata_1.pHash, fileMetadata_2.pHash); - console.log(`pHash distance: ${pHashDistance}`, "\n"); - - // purge Cache and purgeCache status - const purgeCacheResponse = await purgeCache(imagekit, filesList[0].url); - console.log("Purge Cache Response: ", JSON.stringify(purgeCacheResponse, undefined, 2), "\n"); - - const purgeStatus = await getPurgeCacheStatus(imagekit, purgeCacheResponse.requestId); - console.log("Purge Response: ", JSON.stringify(purgeStatus, undefined, 2), "\n"); - - // Bulk add tags - let fileIds = filesList.map((file) => file.fileId); - fileIds.shift(); - var tags = ["red", "blue"]; - const bulkAddTagsResponse = await bulkAddTags(imagekit, fileIds, tags); - console.log("Bulk add tags response: ", bulkAddTagsResponse, "\n"); - - // Bulk remove tags - const bulkRemoveTagsResponse = await bulkRemoveTags(imagekit, fileIds, tags); - console.log("Bulk remove tags response: ", bulkRemoveTagsResponse, "\n"); - - // Create folder - const createFolderResponse_1 = await createFolder(imagekit, "folder1", "/"); - const createFolderResponse_2 = await createFolder(imagekit, "folder2", "/"); - console.log("Folder creation response: ", createFolderResponse_2, "\n"); - - // Copy file - const copyFileResponse = await copyFile(imagekit, fileDetails_1.filePath, "/folder1/"); - console.log("File copy response: ", copyFileResponse, "\n"); - - // Move file - const moveFileResponse = await moveFile(imagekit, `/folder1/${fileDetails_1.name}`, "/folder2/"); - console.log("File move response: ", moveFileResponse, "\n"); - - // Copy folder - const copyFolderResponse = await copyFolder(imagekit, "/folder2", "/folder1/"); - console.log("Copy folder response: ", JSON.stringify(copyFolderResponse, undefined, 2), "\n"); - - // Move folder - const moveFolderResponse = await moveFolder(imagekit, "/folder1", "/folder2/"); - console.log("Move folder response: ", JSON.stringify(moveFolderResponse, undefined, 2), "\n"); - - // Get bulk job status - const getBulkJobStatusResponse = await getBulkJobStatus(imagekit, moveFolderResponse.jobId); - console.log("Bulk job status response: ", JSON.stringify(getBulkJobStatusResponse), "\n"); - - // Delete folder - const deleteFolderResponse = await deleteFolder(imagekit, "/folder2/"); - console.log("Delete folder response: ", deleteFolderResponse, "\n"); - - // Deleting Files - const deleteResponse = await deleteFile(imagekit, fileDetails_1.fileId); - console.log("Deletion response: ", deleteResponse, "\n"); - - // Bulk Delete Files - const bulkDeleteResponse = await bulkDeleteFiles(imagekit, fileIds); - console.log("Bulk deletion response: ", bulkDeleteResponse, "\n"); - - //Authentication token - const authenticationParameters = imagekit.getAuthenticationParameters("your_token"); - console.log("Authentication Parameters: ", JSON.stringify(authenticationParameters, undefined, 2), "\n"); - - process.exit(0); - } catch (err) { - console.log("Encounterted Error: ", JSON.stringify(err, undefined, 2)); - process.exit(1); - } -}; - -const uploadLocalFile = async (imagekitInstance, filePath, fileName) => { - const file = fs.createReadStream(filePath); - const response = await imagekitInstance.upload({ file, fileName }); - return response; -}; - -const uploadFileBuffer = async (imagekitInstance, filePath, fileName) => { - const buffer = fs.readFileSync(filePath); - const response = await imagekitInstance.upload({ file: buffer, fileName }); - return response; -}; - -const uploadFileBase64 = async (imagekitInstance, filePath, fileName) => { - const file_base64 = fs.readFileSync(filePath, "base64"); - //Uncomment to send extensions parameter - // var extensions = [ - // { - // name: "google-auto-tagging", - // maxTags: 5, - // minConfidence: 95 - // } - // ]; - const response = await imagekitInstance.upload({ file: file_base64, fileName/*, extensions*/}); - return response; -}; - -const uploadFileURL = async (imagekitInstance, url, fileName) => { - const response = await imagekitInstance.upload({ file: url, fileName }); - return response; -}; - -const listFiles = async (imagekitInstance, limit = 10, skip = 0) => { - const response = await imagekitInstance.listFiles({ - limit, - skip, - }); - return response; -}; - -const getFileDetails = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.getFileDetails(fileId); - return response; -}; - -const getFileMetadata = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.getFileMetadata(fileId); - return response; -}; - -const updateFileDetails = async (imagekitInstance, fileId, tags = [], customCoordinates = "", extensions = [], webhookUrl = "") => { - let options = {}; - if (Array.isArray(tags) && tags.length > 0) Object.assign(options, { tags }); - if (typeof customCoordinates === "string" && customCoordinates.length > 0) - Object.assign(options, { customCoordinates }); - if (Array.isArray(extensions) && extensions.length > 0) - Object.assign(options,{ extensions }); - if (typeof webhookUrl === "string" && webhookUrl.length > 0) - Object.assign(options,{ webhookUrl }) - const response = await imagekitInstance.updateFileDetails(fileId, options); - return response; -}; - -const purgeCache = async (imagekitInstance, url) => { - const response = await imagekitInstance.purgeCache(url); - return response; -}; - -const getPurgeCacheStatus = async (imagekitInstance, requestId) => { - const response = await imagekitInstance.getPurgeCacheStatus(requestId); - return response; -}; - -const deleteFile = async (imagekitInstance, fileId) => { - const response = await imagekitInstance.deleteFile(fileId); - return "success"; -}; - -const bulkDeleteFiles = async (imagekitInstance, fileIds) => { - const response = await imagekitInstance.bulkDeleteFiles(fileIds); - return "success"; -}; - -const bulkAddTags = async (imagekitInstance, fileIds, tags) => { - const response = await imagekitInstance.bulkAddTags(fileIds, tags); - return "success"; -}; - -const bulkRemoveTags = async (imagekitInstance, fileIds, tags) => { - const response = await imagekitInstance.bulkRemoveTags(fileIds, tags); - return "success"; -}; - -const copyFile = async (imagekitInstance, sourceFilePath, destinationPath) => { - const response = await imagekitInstance.copyFile(sourceFilePath, destinationPath); - return "success"; -}; - -const moveFile = async (imagekitInstance, sourceFilePath, destinationPath) => { - const response = await imagekitInstance.moveFile(sourceFilePath, destinationPath); - return "success"; -}; - -const copyFolder = async (imagekitInstance, sourceFolderPath, destinationPath) => { - const response = await imagekitInstance.copyFolder(sourceFolderPath, destinationPath); - return response; -}; - -const moveFolder = async (imagekitInstance, sourceFolderPath, destinationPath) => { - const response = await imagekitInstance.moveFolder(sourceFolderPath, destinationPath); - return response; -}; - -const createFolder = async (imagekitInstance, folderName, parentFolderPath) => { - const response = await imagekitInstance.createFolder(folderName, parentFolderPath); - return "success"; -}; - -const deleteFolder = async (imagekitInstance, folderPath) => { - const response = await imagekitInstance.deleteFolder(folderPath); - return "success"; -}; - -const getBulkJobStatus = async (imagekitInstance, jobId) => { - const response = await imagekitInstance.getBulkJobStatus(jobId); - return response; -}; - -sampleApp(); diff --git a/sample/package.json b/sample/package.json deleted file mode 100644 index fb22c514..00000000 --- a/sample/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "imagekit": "*" - }, - "author": "", - "license": "ISC" -} \ No newline at end of file diff --git a/sample/test_image.jpg b/sample/test_image.jpg deleted file mode 100644 index 8102e278..00000000 Binary files a/sample/test_image.jpg and /dev/null differ diff --git a/scripts/rename-to-esm-files.js b/scripts/rename-to-esm-files.js new file mode 100644 index 00000000..dc1df1cb --- /dev/null +++ b/scripts/rename-to-esm-files.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node + +const fs = require("fs").promises; +const path = require("path"); + +const extensionMap = { + ".js": ".mjs", + ".d.ts": ".d.mts", +}; +const oldExtensions = Object.keys(extensionMap); + +async function findFiles(rootPath) { + const files = []; + + async function scan(directory) { + const entries = await fs.readdir(directory, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(directory, entry.name); + + if (entry.isDirectory()) { + if (entry.name !== "node_modules" && !entry.name.startsWith(".")) { + await scan(fullPath); + } + } else if (entry.isFile()) { + if (oldExtensions.some((ext) => entry.name.endsWith(ext))) { + files.push(fullPath); + } + } + } + } + + await scan(rootPath); + return files; +} + +async function updateFiles(files) { + const updatedFiles = []; + for (const file of files) { + const updated = await updateFileContents(file); + updatedFiles.push(updated); + } + + console.log(`Updated imports in ${updatedFiles.length} files.`); +} + +async function updateFileContents(file) { + const content = await fs.readFile(file, "utf8"); + + let newContent = content; + // Update each extension type defined in the map + for (const [oldExt, newExt] of Object.entries(extensionMap)) { + // Handle static imports/exports + const staticRegex = new RegExp(`(import|export)(.+from\\s+['"])(\\.\\.?\\/[^'"]+)(\\${oldExt})(['"])`, "g"); + newContent = newContent.replace(staticRegex, `$1$2$3${newExt}$5`); + + // Handle dynamic imports (yield import, await import, regular import()) + const dynamicRegex = new RegExp( + `(yield\\s+import|await\\s+import|import)\\s*\\(\\s*['"](\\.\\.\?\\/[^'"]+)(\\${oldExt})['"]\\s*\\)`, + "g", + ); + newContent = newContent.replace(dynamicRegex, `$1("$2${newExt}")`); + } + + if (content !== newContent) { + await fs.writeFile(file, newContent, "utf8"); + return true; + } + return false; +} + +async function renameFiles(files) { + let counter = 0; + for (const file of files) { + const ext = oldExtensions.find((ext) => file.endsWith(ext)); + const newExt = extensionMap[ext]; + + if (newExt) { + const newPath = file.slice(0, -ext.length) + newExt; + await fs.rename(file, newPath); + counter++; + } + } + + console.log(`Renamed ${counter} files.`); +} + +async function main() { + try { + const targetDir = process.argv[2]; + if (!targetDir) { + console.error("Please provide a target directory"); + process.exit(1); + } + + const targetPath = path.resolve(targetDir); + const targetStats = await fs.stat(targetPath); + + if (!targetStats.isDirectory()) { + console.error("The provided path is not a directory"); + process.exit(1); + } + + console.log(`Scanning directory: ${targetDir}`); + + const files = await findFiles(targetDir); + + if (files.length === 0) { + console.log("No matching files found."); + process.exit(0); + } + + console.log(`Found ${files.length} files.`); + await updateFiles(files); + await renameFiles(files); + console.log("\nDone!"); + } catch (error) { + console.error("An error occurred:", error.message); + process.exit(1); + } +} + +main(); diff --git a/src/Client.ts b/src/Client.ts new file mode 100644 index 00000000..b74fbcd5 --- /dev/null +++ b/src/Client.ts @@ -0,0 +1,95 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "./environments.js"; +import * as core from "./core/index.js"; +import { mergeHeaders } from "./core/headers.js"; +import { Files } from "./api/resources/files/client/Client.js"; +import { CustomMetadataFields } from "./api/resources/customMetadataFields/client/Client.js"; +import { Assets } from "./api/resources/assets/client/Client.js"; +import { Folders } from "./api/resources/folders/client/Client.js"; +import { Accounts } from "./api/resources/accounts/client/Client.js"; +import { Beta } from "./api/resources/beta/client/Client.js"; +import { Cache } from "./api/resources/cache/client/Client.js"; + +export declare namespace ImageKitClient { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class ImageKitClient { + protected readonly _options: ImageKitClient.Options; + protected _files: Files | undefined; + protected _customMetadataFields: CustomMetadataFields | undefined; + protected _assets: Assets | undefined; + protected _folders: Folders | undefined; + protected _accounts: Accounts | undefined; + protected _beta: Beta | undefined; + protected _cache: Cache | undefined; + + constructor(_options: ImageKitClient.Options) { + this._options = { + ..._options, + headers: mergeHeaders( + { + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "imagekit", + "X-Fern-SDK-Version": "6.0.1", + "User-Agent": "imagekit/6.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + _options?.headers, + ), + }; + } + + public get files(): Files { + return (this._files ??= new Files(this._options)); + } + + public get customMetadataFields(): CustomMetadataFields { + return (this._customMetadataFields ??= new CustomMetadataFields(this._options)); + } + + public get assets(): Assets { + return (this._assets ??= new Assets(this._options)); + } + + public get folders(): Folders { + return (this._folders ??= new Folders(this._options)); + } + + public get accounts(): Accounts { + return (this._accounts ??= new Accounts(this._options)); + } + + public get beta(): Beta { + return (this._beta ??= new Beta(this._options)); + } + + public get cache(): Cache { + return (this._cache ??= new Cache(this._options)); + } +} diff --git a/src/api/errors/BadRequestError.ts b/src/api/errors/BadRequestError.ts new file mode 100644 index 00000000..d33b883c --- /dev/null +++ b/src/api/errors/BadRequestError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class BadRequestError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "BadRequestError", + statusCode: 400, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, BadRequestError.prototype); + } +} diff --git a/src/api/errors/ConflictError.ts b/src/api/errors/ConflictError.ts new file mode 100644 index 00000000..cbe77d7c --- /dev/null +++ b/src/api/errors/ConflictError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class ConflictError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "ConflictError", + statusCode: 409, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, ConflictError.prototype); + } +} diff --git a/src/api/errors/ForbiddenError.ts b/src/api/errors/ForbiddenError.ts new file mode 100644 index 00000000..7534e402 --- /dev/null +++ b/src/api/errors/ForbiddenError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class ForbiddenError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "ForbiddenError", + statusCode: 403, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, ForbiddenError.prototype); + } +} diff --git a/src/api/errors/LockedError.ts b/src/api/errors/LockedError.ts new file mode 100644 index 00000000..6aea4953 --- /dev/null +++ b/src/api/errors/LockedError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class LockedError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "LockedError", + statusCode: 423, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, LockedError.prototype); + } +} diff --git a/src/api/errors/NotFoundError.ts b/src/api/errors/NotFoundError.ts new file mode 100644 index 00000000..2a451c05 --- /dev/null +++ b/src/api/errors/NotFoundError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class NotFoundError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "NotFoundError", + statusCode: 404, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, NotFoundError.prototype); + } +} diff --git a/src/api/errors/TooManyRequestsError.ts b/src/api/errors/TooManyRequestsError.ts new file mode 100644 index 00000000..7aaa7df9 --- /dev/null +++ b/src/api/errors/TooManyRequestsError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class TooManyRequestsError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "TooManyRequestsError", + statusCode: 429, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, TooManyRequestsError.prototype); + } +} diff --git a/src/api/errors/UnauthorizedError.ts b/src/api/errors/UnauthorizedError.ts new file mode 100644 index 00000000..3a07a93b --- /dev/null +++ b/src/api/errors/UnauthorizedError.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as errors from "../../errors/index.js"; +import * as core from "../../core/index.js"; + +export class UnauthorizedError extends errors.ImageKitError { + constructor(body?: unknown, rawResponse?: core.RawResponse) { + super({ + message: "UnauthorizedError", + statusCode: 401, + body: body, + rawResponse: rawResponse, + }); + Object.setPrototypeOf(this, UnauthorizedError.prototype); + } +} diff --git a/src/api/errors/index.ts b/src/api/errors/index.ts new file mode 100644 index 00000000..df15b79f --- /dev/null +++ b/src/api/errors/index.ts @@ -0,0 +1,7 @@ +export * from "./BadRequestError.js"; +export * from "./UnauthorizedError.js"; +export * from "./ForbiddenError.js"; +export * from "./TooManyRequestsError.js"; +export * from "./NotFoundError.js"; +export * from "./ConflictError.js"; +export * from "./LockedError.js"; diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 00000000..72cddbe7 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,3 @@ +export * from "./resources/index.js"; +export * from "./types/index.js"; +export * from "./errors/index.js"; diff --git a/src/api/resources/accounts/client/Client.ts b/src/api/resources/accounts/client/Client.ts new file mode 100644 index 00000000..2509bbc1 --- /dev/null +++ b/src/api/resources/accounts/client/Client.ts @@ -0,0 +1,44 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import { Usage } from "../resources/usage/client/Client.js"; +import { Origins } from "../resources/origins/client/Client.js"; +import { UrlEndpoints } from "../resources/urlEndpoints/client/Client.js"; + +export declare namespace Accounts { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } +} + +export class Accounts { + protected readonly _options: Accounts.Options; + protected _usage: Usage | undefined; + protected _origins: Origins | undefined; + protected _urlEndpoints: UrlEndpoints | undefined; + + constructor(_options: Accounts.Options) { + this._options = _options; + } + + public get usage(): Usage { + return (this._usage ??= new Usage(this._options)); + } + + public get origins(): Origins { + return (this._origins ??= new Origins(this._options)); + } + + public get urlEndpoints(): UrlEndpoints { + return (this._urlEndpoints ??= new UrlEndpoints(this._options)); + } +} diff --git a/src/api/resources/accounts/client/index.ts b/src/api/resources/accounts/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/accounts/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/accounts/index.ts b/src/api/resources/accounts/index.ts new file mode 100644 index 00000000..02bb7065 --- /dev/null +++ b/src/api/resources/accounts/index.ts @@ -0,0 +1,2 @@ +export * from "./resources/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/accounts/resources/index.ts b/src/api/resources/accounts/resources/index.ts new file mode 100644 index 00000000..728b4188 --- /dev/null +++ b/src/api/resources/accounts/resources/index.ts @@ -0,0 +1,5 @@ +export * as usage from "./usage/index.js"; +export * from "./usage/types/index.js"; +export * as origins from "./origins/index.js"; +export * as urlEndpoints from "./urlEndpoints/index.js"; +export * from "./usage/client/requests/index.js"; diff --git a/src/api/resources/accounts/resources/origins/client/Client.ts b/src/api/resources/accounts/resources/origins/client/Client.ts new file mode 100644 index 00000000..d47567fd --- /dev/null +++ b/src/api/resources/accounts/resources/origins/client/Client.ts @@ -0,0 +1,483 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Origins { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Origins { + protected readonly _options: Origins.Options; + + constructor(_options: Origins.Options) { + this._options = _options; + } + + /** + * **Note:** This API is currently in beta. + * Returns an array of all configured origins for the current account. + * + * @param {Origins.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.origins.list() + */ + public list(requestOptions?: Origins.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(requestOptions)); + } + + private async __list( + requestOptions?: Origins.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/accounts/origins", + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.OriginResponseArray, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/accounts/origins."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Creates a new origin and returns the origin object. + * + * @param {ImageKit.OriginSchema} request + * @param {Origins.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.origins.create({ + * type: "S3", + * name: "US S3 Storage", + * bucket: "product-images", + * accessKey: "AKIAIOSFODNN7EXAMPLE", + * secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + * }) + */ + public create( + request: ImageKit.OriginSchema, + requestOptions?: Origins.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: ImageKit.OriginSchema, + requestOptions?: Origins.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/accounts/origins", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.OriginResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/accounts/origins."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Retrieves the origin identified by `id`. + * + * @param {string} id - Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + * @param {Origins.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.origins.get("id") + */ + public get(id: string, requestOptions?: Origins.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); + } + + private async __get( + id: string, + requestOptions?: Origins.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/origins/${encodeURIComponent(id)}`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.OriginResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/accounts/origins/{id}."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Updates the origin identified by `id` and returns the updated origin object. + * + * @param {string} id - Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + * @param {ImageKit.OriginSchema} request + * @param {Origins.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.origins.update("id", { + * type: "S3", + * name: "US S3 Storage", + * bucket: "product-images", + * accessKey: "AKIAIOSFODNN7EXAMPLE", + * secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + * }) + */ + public update( + id: string, + request: ImageKit.OriginSchema, + requestOptions?: Origins.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: ImageKit.OriginSchema, + requestOptions?: Origins.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/origins/${encodeURIComponent(id)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.OriginResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling PUT /v1/accounts/origins/{id}."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **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. + * + * @param {string} id - Unique identifier for the origin. This is generated by ImageKit when you create a new origin. + * @param {Origins.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.origins.delete("id") + */ + public delete(id: string, requestOptions?: Origins.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete(id: string, requestOptions?: Origins.RequestOptions): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/origins/${encodeURIComponent(id)}`, + ), + method: "DELETE", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling DELETE /v1/accounts/origins/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/accounts/resources/origins/client/index.ts b/src/api/resources/accounts/resources/origins/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/accounts/resources/origins/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/accounts/resources/origins/index.ts b/src/api/resources/accounts/resources/origins/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/accounts/resources/origins/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/accounts/resources/urlEndpoints/client/Client.ts b/src/api/resources/accounts/resources/urlEndpoints/client/Client.ts new file mode 100644 index 00000000..63c1593e --- /dev/null +++ b/src/api/resources/accounts/resources/urlEndpoints/client/Client.ts @@ -0,0 +1,497 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace UrlEndpoints { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class UrlEndpoints { + protected readonly _options: UrlEndpoints.Options; + + constructor(_options: UrlEndpoints.Options) { + this._options = _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. + * + * @param {UrlEndpoints.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.urlEndpoints.list() + */ + public list( + requestOptions?: UrlEndpoints.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(requestOptions)); + } + + private async __list( + requestOptions?: UrlEndpoints.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/accounts/url-endpoints", + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.UrlEndpointResponseArray, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/accounts/url-endpoints."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Creates a new URL‑endpoint and returns the resulting object. + * + * @param {ImageKit.UrlEndpointSchema} request + * @param {UrlEndpoints.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.urlEndpoints.create({ + * description: "My custom URL endpoint", + * urlPrefix: "product-images", + * origins: ["origin-id-1"] + * }) + */ + public create( + request: ImageKit.UrlEndpointSchema, + requestOptions?: UrlEndpoints.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: ImageKit.UrlEndpointSchema, + requestOptions?: UrlEndpoints.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/accounts/url-endpoints", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.UrlEndpointResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/accounts/url-endpoints."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Retrieves the URL‑endpoint identified by `id`. + * + * @param {string} id - 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`. + * @param {UrlEndpoints.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.urlEndpoints.get("id") + */ + public get( + id: string, + requestOptions?: UrlEndpoints.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); + } + + private async __get( + id: string, + requestOptions?: UrlEndpoints.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/url-endpoints/${encodeURIComponent(id)}`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.UrlEndpointResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling GET /v1/accounts/url-endpoints/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **Note:** This API is currently in beta. + * Updates the URL‑endpoint identified by `id` and returns the updated object. + * + * @param {string} id - 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`. + * @param {ImageKit.UrlEndpointSchema} request + * @param {UrlEndpoints.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.urlEndpoints.update("id", { + * description: "My custom URL endpoint", + * urlPrefix: "product-images", + * origins: ["origin-id-1"] + * }) + */ + public update( + id: string, + request: ImageKit.UrlEndpointSchema, + requestOptions?: UrlEndpoints.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: ImageKit.UrlEndpointSchema, + requestOptions?: UrlEndpoints.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/url-endpoints/${encodeURIComponent(id)}`, + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.UrlEndpointResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling PUT /v1/accounts/url-endpoints/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * **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. + * + * @param {string} id - 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`. + * @param {UrlEndpoints.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.urlEndpoints.delete("id") + */ + public delete(id: string, requestOptions?: UrlEndpoints.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete( + id: string, + requestOptions?: UrlEndpoints.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/accounts/url-endpoints/${encodeURIComponent(id)}`, + ), + method: "DELETE", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling DELETE /v1/accounts/url-endpoints/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/accounts/resources/urlEndpoints/client/index.ts b/src/api/resources/accounts/resources/urlEndpoints/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/accounts/resources/urlEndpoints/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/accounts/resources/urlEndpoints/index.ts b/src/api/resources/accounts/resources/urlEndpoints/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/accounts/resources/urlEndpoints/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/accounts/resources/usage/client/Client.ts b/src/api/resources/accounts/resources/usage/client/Client.ts new file mode 100644 index 00000000..bedf75d4 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/client/Client.ts @@ -0,0 +1,140 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Usage { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Usage { + protected readonly _options: Usage.Options; + + constructor(_options: Usage.Options) { + this._options = _options; + } + + /** + * 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. + * + * @param {ImageKit.accounts.UsageGetRequest} request + * @param {Usage.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.accounts.usage.get({ + * startDate: "startDate", + * endDate: "endDate" + * }) + */ + public get( + request: ImageKit.accounts.UsageGetRequest, + requestOptions?: Usage.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(request, requestOptions)); + } + + private async __get( + request: ImageKit.accounts.UsageGetRequest, + requestOptions?: Usage.RequestOptions, + ): Promise> { + const { startDate, endDate } = request; + const _queryParams: Record = {}; + _queryParams["startDate"] = startDate; + _queryParams["endDate"] = endDate; + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/accounts/usage", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...requestOptions?.queryParams }, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.accounts.UsageGetResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/accounts/usage."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/accounts/resources/usage/client/index.ts b/src/api/resources/accounts/resources/usage/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/accounts/resources/usage/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/accounts/resources/usage/client/requests/UsageGetRequest.ts b/src/api/resources/accounts/resources/usage/client/requests/UsageGetRequest.ts new file mode 100644 index 00000000..8476dd30 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/client/requests/UsageGetRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * startDate: "startDate", + * endDate: "endDate" + * } + */ +export interface UsageGetRequest { + /** 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; + /** 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; +} diff --git a/src/api/resources/accounts/resources/usage/client/requests/index.ts b/src/api/resources/accounts/resources/usage/client/requests/index.ts new file mode 100644 index 00000000..76084c66 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/client/requests/index.ts @@ -0,0 +1 @@ +export { type UsageGetRequest } from "./UsageGetRequest.js"; diff --git a/src/api/resources/accounts/resources/usage/index.ts b/src/api/resources/accounts/resources/usage/index.ts new file mode 100644 index 00000000..f095e147 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/index.ts @@ -0,0 +1,2 @@ +export * from "./types/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/accounts/resources/usage/types/UsageGetResponse.ts b/src/api/resources/accounts/resources/usage/types/UsageGetResponse.ts new file mode 100644 index 00000000..711f0cf9 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/types/UsageGetResponse.ts @@ -0,0 +1,16 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface UsageGetResponse { + /** Amount of bandwidth used in bytes. */ + bandwidthBytes?: number; + /** Storage used by media library in bytes. */ + mediaLibraryStorageBytes?: number; + /** Number of video processing units used. */ + videoProcessingUnitsCount?: number; + /** Number of extension units used. */ + extensionUnitsCount?: number; + /** Storage used by the original cache in bytes. */ + originalCacheStorageBytes?: number; +} diff --git a/src/api/resources/accounts/resources/usage/types/index.ts b/src/api/resources/accounts/resources/usage/types/index.ts new file mode 100644 index 00000000..9ac58ed7 --- /dev/null +++ b/src/api/resources/accounts/resources/usage/types/index.ts @@ -0,0 +1 @@ +export * from "./UsageGetResponse.js"; diff --git a/src/api/resources/assets/client/Client.ts b/src/api/resources/assets/client/Client.ts new file mode 100644 index 00000000..75d11b14 --- /dev/null +++ b/src/api/resources/assets/client/Client.ts @@ -0,0 +1,163 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import * as ImageKit from "../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js"; +import * as errors from "../../../../errors/index.js"; + +export declare namespace Assets { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Assets { + protected readonly _options: Assets.Options; + + constructor(_options: Assets.Options) { + this._options = _options; + } + + /** + * 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`. + * + * @param {ImageKit.AssetsListRequest} request + * @param {Assets.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.assets.list() + */ + public list( + request: ImageKit.AssetsListRequest = {}, + requestOptions?: Assets.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(request, requestOptions)); + } + + private async __list( + request: ImageKit.AssetsListRequest = {}, + requestOptions?: Assets.RequestOptions, + ): Promise> { + const { type: type_, sort, path, searchQuery, fileType, limit, skip } = request; + const _queryParams: Record = {}; + if (type_ != null) { + _queryParams["type"] = type_; + } + + if (sort != null) { + _queryParams["sort"] = sort; + } + + if (path != null) { + _queryParams["path"] = path; + } + + if (searchQuery != null) { + _queryParams["searchQuery"] = searchQuery; + } + + if (fileType != null) { + _queryParams["fileType"] = fileType; + } + + if (limit != null) { + _queryParams["limit"] = limit.toString(); + } + + if (skip != null) { + _queryParams["skip"] = skip.toString(); + } + + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...requestOptions?.queryParams }, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.AssetsListResponseItem[], rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/assets/client/index.ts b/src/api/resources/assets/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/assets/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/assets/client/requests/AssetsListRequest.ts b/src/api/resources/assets/client/requests/AssetsListRequest.ts new file mode 100644 index 00000000..a57d5c62 --- /dev/null +++ b/src/api/resources/assets/client/requests/AssetsListRequest.ts @@ -0,0 +1,54 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../../../../index.js"; + +/** + * @example + * {} + */ +export interface AssetsListRequest { + /** + * 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?: ImageKit.AssetsListRequestType; + /** Sort the results by one of the supported fields in ascending or descending order. */ + sort?: ImageKit.AssetsListRequestSort; + /** + * 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; + /** + * 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?: ImageKit.AssetsListRequestFileType; + /** The maximum number of results to return in response. */ + limit?: number; + /** The number of results to skip before returning results. */ + skip?: number; +} diff --git a/src/api/resources/assets/client/requests/index.ts b/src/api/resources/assets/client/requests/index.ts new file mode 100644 index 00000000..f35e06a1 --- /dev/null +++ b/src/api/resources/assets/client/requests/index.ts @@ -0,0 +1 @@ +export { type AssetsListRequest } from "./AssetsListRequest.js"; diff --git a/src/api/resources/assets/index.ts b/src/api/resources/assets/index.ts new file mode 100644 index 00000000..f095e147 --- /dev/null +++ b/src/api/resources/assets/index.ts @@ -0,0 +1,2 @@ +export * from "./types/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/assets/types/AssetsListRequestFileType.ts b/src/api/resources/assets/types/AssetsListRequestFileType.ts new file mode 100644 index 00000000..e4b72602 --- /dev/null +++ b/src/api/resources/assets/types/AssetsListRequestFileType.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export type AssetsListRequestFileType = "all" | "image" | "non-image"; +export const AssetsListRequestFileType = { + All: "all", + Image: "image", + NonImage: "non-image", +} as const; diff --git a/src/api/resources/assets/types/AssetsListRequestSort.ts b/src/api/resources/assets/types/AssetsListRequestSort.ts new file mode 100644 index 00000000..40d030fe --- /dev/null +++ b/src/api/resources/assets/types/AssetsListRequestSort.ts @@ -0,0 +1,35 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export type AssetsListRequestSort = + | "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"; +export const AssetsListRequestSort = { + AscName: "ASC_NAME", + DescName: "DESC_NAME", + AscCreated: "ASC_CREATED", + DescCreated: "DESC_CREATED", + AscUpdated: "ASC_UPDATED", + DescUpdated: "DESC_UPDATED", + AscHeight: "ASC_HEIGHT", + DescHeight: "DESC_HEIGHT", + AscWidth: "ASC_WIDTH", + DescWidth: "DESC_WIDTH", + AscSize: "ASC_SIZE", + DescSize: "DESC_SIZE", + AscRelevance: "ASC_RELEVANCE", + DescRelevance: "DESC_RELEVANCE", +} as const; diff --git a/src/api/resources/assets/types/AssetsListRequestType.ts b/src/api/resources/assets/types/AssetsListRequestType.ts new file mode 100644 index 00000000..e731a5cd --- /dev/null +++ b/src/api/resources/assets/types/AssetsListRequestType.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export type AssetsListRequestType = "file" | "file-version" | "folder" | "all"; +export const AssetsListRequestType = { + File: "file", + FileVersion: "file-version", + Folder: "folder", + All: "all", +} as const; diff --git a/src/api/resources/assets/types/AssetsListResponseItem.ts b/src/api/resources/assets/types/AssetsListResponseItem.ts new file mode 100644 index 00000000..7e549235 --- /dev/null +++ b/src/api/resources/assets/types/AssetsListResponseItem.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../../../index.js"; + +export type AssetsListResponseItem = ImageKit.FileDetails | ImageKit.Folder; diff --git a/src/api/resources/assets/types/index.ts b/src/api/resources/assets/types/index.ts new file mode 100644 index 00000000..f27618e4 --- /dev/null +++ b/src/api/resources/assets/types/index.ts @@ -0,0 +1,4 @@ +export * from "./AssetsListRequestType.js"; +export * from "./AssetsListRequestSort.js"; +export * from "./AssetsListRequestFileType.js"; +export * from "./AssetsListResponseItem.js"; diff --git a/src/api/resources/beta/client/Client.ts b/src/api/resources/beta/client/Client.ts new file mode 100644 index 00000000..292a51ff --- /dev/null +++ b/src/api/resources/beta/client/Client.ts @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import { V2 } from "../resources/v2/client/Client.js"; + +export declare namespace Beta { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } +} + +export class Beta { + protected readonly _options: Beta.Options; + protected _v2: V2 | undefined; + + constructor(_options: Beta.Options) { + this._options = _options; + } + + public get v2(): V2 { + return (this._v2 ??= new V2(this._options)); + } +} diff --git a/src/api/resources/beta/client/index.ts b/src/api/resources/beta/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/beta/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/beta/index.ts b/src/api/resources/beta/index.ts new file mode 100644 index 00000000..9eb1192d --- /dev/null +++ b/src/api/resources/beta/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/src/api/resources/beta/resources/index.ts b/src/api/resources/beta/resources/index.ts new file mode 100644 index 00000000..f8ea42c1 --- /dev/null +++ b/src/api/resources/beta/resources/index.ts @@ -0,0 +1 @@ +export * as v2 from "./v2/index.js"; diff --git a/src/api/resources/beta/resources/v2/client/Client.ts b/src/api/resources/beta/resources/v2/client/Client.ts new file mode 100644 index 00000000..745e05b5 --- /dev/null +++ b/src/api/resources/beta/resources/v2/client/Client.ts @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import { Files } from "../resources/files/client/Client.js"; + +export declare namespace V2 { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } +} + +export class V2 { + protected readonly _options: V2.Options; + protected _files: Files | undefined; + + constructor(_options: V2.Options) { + this._options = _options; + } + + public get files(): Files { + return (this._files ??= new Files(this._options)); + } +} diff --git a/src/api/resources/beta/resources/v2/client/index.ts b/src/api/resources/beta/resources/v2/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/beta/resources/v2/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/beta/resources/v2/index.ts b/src/api/resources/beta/resources/v2/index.ts new file mode 100644 index 00000000..9eb1192d --- /dev/null +++ b/src/api/resources/beta/resources/v2/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/src/api/resources/beta/resources/v2/resources/files/client/Client.ts b/src/api/resources/beta/resources/v2/resources/files/client/Client.ts new file mode 100644 index 00000000..525f3d35 --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/files/client/Client.ts @@ -0,0 +1,237 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../../../environments.js"; +import * as core from "../../../../../../../../core/index.js"; +import * as ImageKit from "../../../../../../../index.js"; +import * as fs from "fs"; +import { toJson } from "../../../../../../../../core/json.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../../../core/headers.js"; +import * as errors from "../../../../../../../../errors/index.js"; + +export declare namespace Files { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Files { + protected readonly _options: Files.Options; + + constructor(_options: Files.Options) { + this._options = _options; + } + + /** + * 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. + * + * @param {ImageKit.beta.v2.FileUploadV2} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * + * @example + * import { createReadStream } from "fs"; + * await client.beta.v2.files.upload({ + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * }) + */ + public upload( + request: ImageKit.beta.v2.FileUploadV2, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__upload(request, requestOptions)); + } + + private async __upload( + request: ImageKit.beta.v2.FileUploadV2, + requestOptions?: Files.RequestOptions, + ): Promise> { + const _request = await core.newFormData(); + await _request.appendFile("file", request.file); + _request.append("fileName", request.fileName); + if (request.token != null) { + _request.append("token", request.token); + } + + if (request.useUniqueFileName != null) { + _request.append("useUniqueFileName", request.useUniqueFileName.toString()); + } + + if (request.tags != null) { + for (const _item of request.tags) { + _request.append("tags", _item); + } + } + + if (request.folder != null) { + _request.append("folder", request.folder); + } + + if (request.isPrivateFile != null) { + _request.append("isPrivateFile", request.isPrivateFile.toString()); + } + + if (request.isPublished != null) { + _request.append("isPublished", request.isPublished.toString()); + } + + if (request.customCoordinates != null) { + _request.append("customCoordinates", request.customCoordinates); + } + + if (request.responseFields != null) { + for (const _item of request.responseFields) { + _request.append("responseFields", _item); + } + } + + if (request.extensions != null) { + for (const _item of request.extensions) { + _request.append("extensions", typeof _item === "string" ? _item : toJson(_item)); + } + } + + if (request.webhookUrl != null) { + _request.append("webhookUrl", request.webhookUrl); + } + + if (request.overwriteFile != null) { + _request.append("overwriteFile", request.overwriteFile.toString()); + } + + if (request.overwriteAITags != null) { + _request.append("overwriteAITags", request.overwriteAITags.toString()); + } + + if (request.overwriteTags != null) { + _request.append("overwriteTags", request.overwriteTags.toString()); + } + + if (request.overwriteCustomMetadata != null) { + _request.append("overwriteCustomMetadata", request.overwriteCustomMetadata.toString()); + } + + if (request.customMetadata != null) { + _request.append("customMetadata", toJson(request.customMetadata)); + } + + if (request.transformation != null) { + _request.append("transformation", toJson(request.transformation)); + } + + if (request.checks != null) { + _request.append("checks", request.checks); + } + + if (request.description != null) { + _request.append("description", request.description); + } + + const _maybeEncodedRequest = await _request.getRequest(); + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ + Authorization: await this._getAuthorizationHeader(), + ..._maybeEncodedRequest.headers, + }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "api/v2/files/upload", + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "file", + duplex: _maybeEncodedRequest.duplex, + body: _maybeEncodedRequest.body, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.Upload, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /api/v2/files/upload."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/beta/resources/v2/resources/files/client/index.ts b/src/api/resources/beta/resources/v2/resources/files/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/files/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/beta/resources/v2/resources/files/client/requests/FileUploadV2.ts b/src/api/resources/beta/resources/v2/resources/files/client/requests/FileUploadV2.ts new file mode 100644 index 00000000..01b47ee0 --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/files/client/requests/FileUploadV2.ts @@ -0,0 +1,146 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as fs from "fs"; +import * as core from "../../../../../../../../../core/index.js"; +import * as ImageKit from "../../../../../../../../index.js"; + +/** + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + * + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + * + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + */ +export interface FileUploadV2 { + /** + * 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: core.file.Uploadable.FileLike; + /** 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; + /** + * 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; + /** + * 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?: string[]; + /** 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; + /** + * 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; + /** Array of response field keys to include in the API response body. */ + responseFields?: FileUploadV2.ResponseFields.Item[]; + /** Array of extensions to be applied to the image. Each extension can be configured with specific parameters based on the extension type. */ + extensions?: FileUploadV2.Extensions.Item[]; + /** 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; + /** 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 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 `tags`, and a file already exists at the exact location, existing tags will be removed. */ + overwriteTags?: boolean; + /** If the request does not have `customMetadata`, and a file already exists at the exact location, existing customMetadata will be removed. */ + overwriteCustomMetadata?: boolean; + /** JSON key-value pairs to associate with the asset. Create the custom metadata fields before setting these values. */ + customMetadata?: Record; + transformation?: ImageKit.TransformationObject; + /** + * 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; + /** Optional text to describe the contents of the file. */ + description?: string; +} + +export namespace FileUploadV2 { + export type ResponseFields = ResponseFields.Item[]; + + export namespace ResponseFields { + export type Item = + | "tags" + | "customCoordinates" + | "isPrivateFile" + | "embeddedMetadata" + | "isPublished" + | "customMetadata" + | "metadata"; + export const Item = { + Tags: "tags", + CustomCoordinates: "customCoordinates", + IsPrivateFile: "isPrivateFile", + EmbeddedMetadata: "embeddedMetadata", + IsPublished: "isPublished", + CustomMetadata: "customMetadata", + Metadata: "metadata", + } as const; + } + + export type Extensions = Extensions.Item[]; + + export namespace Extensions { + export type Item = + | ImageKit.RemovedotBgExtension + | ImageKit.AutoTaggingExtension + | ImageKit.AutoDescriptionExtension; + } +} diff --git a/src/api/resources/beta/resources/v2/resources/files/client/requests/index.ts b/src/api/resources/beta/resources/v2/resources/files/client/requests/index.ts new file mode 100644 index 00000000..dcde346b --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/files/client/requests/index.ts @@ -0,0 +1 @@ +export { type FileUploadV2 } from "./FileUploadV2.js"; diff --git a/src/api/resources/beta/resources/v2/resources/files/index.ts b/src/api/resources/beta/resources/v2/resources/files/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/files/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/beta/resources/v2/resources/index.ts b/src/api/resources/beta/resources/v2/resources/index.ts new file mode 100644 index 00000000..dbe70183 --- /dev/null +++ b/src/api/resources/beta/resources/v2/resources/index.ts @@ -0,0 +1,2 @@ +export * as files from "./files/index.js"; +export * from "./files/client/requests/index.js"; diff --git a/src/api/resources/cache/client/Client.ts b/src/api/resources/cache/client/Client.ts new file mode 100644 index 00000000..1b082a1c --- /dev/null +++ b/src/api/resources/cache/client/Client.ts @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import { Invalidation } from "../resources/invalidation/client/Client.js"; + +export declare namespace Cache { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } +} + +export class Cache { + protected readonly _options: Cache.Options; + protected _invalidation: Invalidation | undefined; + + constructor(_options: Cache.Options) { + this._options = _options; + } + + public get invalidation(): Invalidation { + return (this._invalidation ??= new Invalidation(this._options)); + } +} diff --git a/src/api/resources/cache/client/index.ts b/src/api/resources/cache/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/cache/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/cache/index.ts b/src/api/resources/cache/index.ts new file mode 100644 index 00000000..02bb7065 --- /dev/null +++ b/src/api/resources/cache/index.ts @@ -0,0 +1,2 @@ +export * from "./resources/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/cache/resources/index.ts b/src/api/resources/cache/resources/index.ts new file mode 100644 index 00000000..81500d2c --- /dev/null +++ b/src/api/resources/cache/resources/index.ts @@ -0,0 +1,3 @@ +export * as invalidation from "./invalidation/index.js"; +export * from "./invalidation/types/index.js"; +export * from "./invalidation/client/requests/index.js"; diff --git a/src/api/resources/cache/resources/invalidation/client/Client.ts b/src/api/resources/cache/resources/invalidation/client/Client.ts new file mode 100644 index 00000000..da964851 --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/client/Client.ts @@ -0,0 +1,228 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Invalidation { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Invalidation { + protected readonly _options: Invalidation.Options; + + constructor(_options: Invalidation.Options) { + this._options = _options; + } + + /** + * 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. + * + * @param {ImageKit.cache.InvalidationCreateRequest} request + * @param {Invalidation.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.cache.invalidation.create({ + * url: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg" + * }) + */ + public create( + request: ImageKit.cache.InvalidationCreateRequest, + requestOptions?: Invalidation.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: ImageKit.cache.InvalidationCreateRequest, + requestOptions?: Invalidation.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/purge", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + data: _response.body as ImageKit.cache.InvalidationCreateResponse, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/purge."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API returns the status of a purge cache request. + * + * @param {string} requestId - Should be a valid requestId. + * @param {Invalidation.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.cache.invalidation.get("requestId") + */ + public get( + requestId: string, + requestOptions?: Invalidation.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(requestId, requestOptions)); + } + + private async __get( + requestId: string, + requestOptions?: Invalidation.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/purge/${encodeURIComponent(requestId)}`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + data: _response.body as ImageKit.cache.InvalidationGetResponse, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files/purge/{requestId}."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/cache/resources/invalidation/client/index.ts b/src/api/resources/cache/resources/invalidation/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/cache/resources/invalidation/client/requests/InvalidationCreateRequest.ts b/src/api/resources/cache/resources/invalidation/client/requests/InvalidationCreateRequest.ts new file mode 100644 index 00000000..361ca5fd --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/client/requests/InvalidationCreateRequest.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * url: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg" + * } + */ +export interface InvalidationCreateRequest { + /** The full URL of the file to be purged. */ + url: string; +} diff --git a/src/api/resources/cache/resources/invalidation/client/requests/index.ts b/src/api/resources/cache/resources/invalidation/client/requests/index.ts new file mode 100644 index 00000000..55084078 --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/client/requests/index.ts @@ -0,0 +1 @@ +export { type InvalidationCreateRequest } from "./InvalidationCreateRequest.js"; diff --git a/src/api/resources/cache/resources/invalidation/index.ts b/src/api/resources/cache/resources/invalidation/index.ts new file mode 100644 index 00000000..f095e147 --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/index.ts @@ -0,0 +1,2 @@ +export * from "./types/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/cache/resources/invalidation/types/InvalidationCreateResponse.ts b/src/api/resources/cache/resources/invalidation/types/InvalidationCreateResponse.ts new file mode 100644 index 00000000..8a309e95 --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/types/InvalidationCreateResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface InvalidationCreateResponse { + /** Unique identifier of the purge request. This can be used to check the status of the purge request. */ + requestId?: string; +} diff --git a/src/api/resources/cache/resources/invalidation/types/InvalidationGetResponse.ts b/src/api/resources/cache/resources/invalidation/types/InvalidationGetResponse.ts new file mode 100644 index 00000000..75df4d8b --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/types/InvalidationGetResponse.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface InvalidationGetResponse { + /** Status of the purge request. */ + status?: InvalidationGetResponse.Status; +} + +export namespace InvalidationGetResponse { + /** + * Status of the purge request. + */ + export type Status = "Pending" | "Completed"; + export const Status = { + Pending: "Pending", + Completed: "Completed", + } as const; +} diff --git a/src/api/resources/cache/resources/invalidation/types/index.ts b/src/api/resources/cache/resources/invalidation/types/index.ts new file mode 100644 index 00000000..b75c6059 --- /dev/null +++ b/src/api/resources/cache/resources/invalidation/types/index.ts @@ -0,0 +1,2 @@ +export * from "./InvalidationCreateResponse.js"; +export * from "./InvalidationGetResponse.js"; diff --git a/src/api/resources/customMetadataFields/client/Client.ts b/src/api/resources/customMetadataFields/client/Client.ts new file mode 100644 index 00000000..9b4e2b5f --- /dev/null +++ b/src/api/resources/customMetadataFields/client/Client.ts @@ -0,0 +1,421 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import * as ImageKit from "../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js"; +import * as errors from "../../../../errors/index.js"; + +export declare namespace CustomMetadataFields { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class CustomMetadataFields { + protected readonly _options: CustomMetadataFields.Options; + + constructor(_options: CustomMetadataFields.Options) { + this._options = _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. + * + * @param {ImageKit.CustomMetadataFieldsListRequest} request + * @param {CustomMetadataFields.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.customMetadataFields.list() + */ + public list( + request: ImageKit.CustomMetadataFieldsListRequest = {}, + requestOptions?: CustomMetadataFields.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(request, requestOptions)); + } + + private async __list( + request: ImageKit.CustomMetadataFieldsListRequest = {}, + requestOptions?: CustomMetadataFields.RequestOptions, + ): Promise> { + const { includeDeleted } = request; + const _queryParams: Record = {}; + if (includeDeleted != null) { + _queryParams["includeDeleted"] = includeDeleted.toString(); + } + + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/customMetadataFields", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...requestOptions?.queryParams }, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.CustomMetadataField[], rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/customMetadataFields."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.CustomMetadataFieldsCreateRequest} request + * @param {CustomMetadataFields.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.customMetadataFields.create({ + * name: "price", + * label: "price", + * schema: { + * type: "Number", + * minValue: 1000, + * maxValue: 3000 + * } + * }) + */ + public create( + request: ImageKit.CustomMetadataFieldsCreateRequest, + requestOptions?: CustomMetadataFields.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: ImageKit.CustomMetadataFieldsCreateRequest, + requestOptions?: CustomMetadataFields.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/customMetadataFields", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.CustomMetadataField, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/customMetadataFields."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {string} id - Should be a valid custom metadata field id. + * @param {CustomMetadataFields.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.customMetadataFields.delete("id") + */ + public delete( + id: string, + requestOptions?: CustomMetadataFields.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete( + id: string, + requestOptions?: CustomMetadataFields.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/customMetadataFields/${encodeURIComponent(id)}`, + ), + method: "DELETE", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling DELETE /v1/customMetadataFields/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API updates the label or schema of an existing custom metadata field. + * + * @param {string} id - Should be a valid custom metadata field id. + * @param {ImageKit.CustomMetadataFieldsUpdateRequest} request + * @param {CustomMetadataFields.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.customMetadataFields.update("id", { + * label: "price", + * schema: { + * minValue: 1000, + * maxValue: 3000 + * } + * }) + */ + public update( + id: string, + request: ImageKit.CustomMetadataFieldsUpdateRequest = {}, + requestOptions?: CustomMetadataFields.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: ImageKit.CustomMetadataFieldsUpdateRequest = {}, + requestOptions?: CustomMetadataFields.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/customMetadataFields/${encodeURIComponent(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.CustomMetadataField, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling PATCH /v1/customMetadataFields/{id}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/customMetadataFields/client/index.ts b/src/api/resources/customMetadataFields/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/customMetadataFields/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsCreateRequest.ts b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsCreateRequest.ts new file mode 100644 index 00000000..c4a91afb --- /dev/null +++ b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsCreateRequest.ts @@ -0,0 +1,84 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * name: "price", + * label: "price", + * schema: { + * type: "Number", + * minValue: 1000, + * maxValue: 3000 + * } + * } + */ +export interface CustomMetadataFieldsCreateRequest { + /** API name of the custom metadata field. This should be unique across all (including deleted) custom metadata fields. */ + name: string; + /** 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; + schema: CustomMetadataFieldsCreateRequest.Schema; +} + +export namespace CustomMetadataFieldsCreateRequest { + export interface Schema { + /** Type of the custom metadata field. */ + type: Schema.Type; + /** An array of allowed values. This property is only required if `type` property is set to `SingleSelect` or `MultiSelect`. */ + selectOptions?: Schema.SelectOptions.Item[]; + /** 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?: Schema.DefaultValue; + /** 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; + /** 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?: Schema.MinValue; + /** 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?: Schema.MaxValue; + /** Minimum length of string. Only set this property if `type` is set to `Text` or `Textarea`. */ + minLength?: number; + /** Maximum length of string. Only set this property if `type` is set to `Text` or `Textarea`. */ + maxLength?: number; + } + + export namespace Schema { + /** + * Type of the custom metadata field. + */ + export type Type = "Text" | "Textarea" | "Number" | "Date" | "Boolean" | "SingleSelect" | "MultiSelect"; + export const Type = { + Text: "Text", + Textarea: "Textarea", + Number: "Number", + Date: "Date", + Boolean: "Boolean", + SingleSelect: "SingleSelect", + MultiSelect: "MultiSelect", + } as const; + export type SelectOptions = SelectOptions.Item[]; + + export namespace SelectOptions { + export type Item = string | number | boolean; + } + + /** + * 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. + */ + export type DefaultValue = + | string + | number + | boolean + /** + * Default value should be of type array when custom metadata field type is set to `MultiSelect`. */ + | (string | number | boolean)[]; + /** + * 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. + */ + export type MinValue = string | 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. + */ + export type MaxValue = string | number; + } +} diff --git a/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsListRequest.ts b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsListRequest.ts new file mode 100644 index 00000000..db259e94 --- /dev/null +++ b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsListRequest.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * {} + */ +export interface CustomMetadataFieldsListRequest { + /** Set it to `true` to include deleted field objects in the API response. */ + includeDeleted?: boolean; +} diff --git a/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsUpdateRequest.ts b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsUpdateRequest.ts new file mode 100644 index 00000000..a82ce527 --- /dev/null +++ b/src/api/resources/customMetadataFields/client/requests/CustomMetadataFieldsUpdateRequest.ts @@ -0,0 +1,69 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * label: "price", + * schema: { + * minValue: 1000, + * maxValue: 3000 + * } + * } + */ +export interface CustomMetadataFieldsUpdateRequest { + /** 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?: CustomMetadataFieldsUpdateRequest.Schema; +} + +export namespace CustomMetadataFieldsUpdateRequest { + /** + * 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 { + /** An array of allowed values. This property is only required if `type` property is set to `SingleSelect` or `MultiSelect`. */ + selectOptions?: Schema.SelectOptions.Item[]; + /** 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?: Schema.DefaultValue; + /** 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; + /** 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?: Schema.MinValue; + /** 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?: Schema.MaxValue; + /** Minimum length of string. Only set this property if `type` is set to `Text` or `Textarea`. */ + minLength?: number; + /** Maximum length of string. Only set this property if `type` is set to `Text` or `Textarea`. */ + maxLength?: number; + } + + export namespace Schema { + export type SelectOptions = SelectOptions.Item[]; + + export namespace SelectOptions { + export type Item = string | number | boolean; + } + + /** + * 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. + */ + export type DefaultValue = + | string + | number + | boolean + /** + * Default value should be of type array when custom metadata field type is set to `MultiSelect`. */ + | (string | number | boolean)[]; + /** + * 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. + */ + export type MinValue = string | 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. + */ + export type MaxValue = string | number; + } +} diff --git a/src/api/resources/customMetadataFields/client/requests/index.ts b/src/api/resources/customMetadataFields/client/requests/index.ts new file mode 100644 index 00000000..7e9b45d3 --- /dev/null +++ b/src/api/resources/customMetadataFields/client/requests/index.ts @@ -0,0 +1,3 @@ +export { type CustomMetadataFieldsListRequest } from "./CustomMetadataFieldsListRequest.js"; +export { type CustomMetadataFieldsCreateRequest } from "./CustomMetadataFieldsCreateRequest.js"; +export { type CustomMetadataFieldsUpdateRequest } from "./CustomMetadataFieldsUpdateRequest.js"; diff --git a/src/api/resources/customMetadataFields/index.ts b/src/api/resources/customMetadataFields/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/customMetadataFields/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/files/client/Client.ts b/src/api/resources/files/client/Client.ts new file mode 100644 index 00000000..c35f07b4 --- /dev/null +++ b/src/api/resources/files/client/Client.ts @@ -0,0 +1,842 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import * as ImageKit from "../../../index.js"; +import * as fs from "fs"; +import { toJson } from "../../../../core/json.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js"; +import * as errors from "../../../../errors/index.js"; +import { Bulk } from "../resources/bulk/client/Client.js"; +import { Versions } from "../resources/versions/client/Client.js"; +import { Metadata } from "../resources/metadata/client/Client.js"; + +export declare namespace Files { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Files { + protected readonly _options: Files.Options; + protected _bulk: Bulk | undefined; + protected _versions: Versions | undefined; + protected _metadata: Metadata | undefined; + + constructor(_options: Files.Options) { + this._options = _options; + } + + public get bulk(): Bulk { + return (this._bulk ??= new Bulk(this._options)); + } + + public get versions(): Versions { + return (this._versions ??= new Versions(this._options)); + } + + public get metadata(): Metadata { + return (this._metadata ??= new Metadata(this._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 `expiration` 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. + * + * @param {ImageKit.FileUploadV1} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * + * @example + * import { createReadStream } from "fs"; + * await client.files.upload({ + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * }) + */ + public upload( + request: ImageKit.FileUploadV1, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__upload(request, requestOptions)); + } + + private async __upload( + request: ImageKit.FileUploadV1, + requestOptions?: Files.RequestOptions, + ): Promise> { + const _request = await core.newFormData(); + await _request.appendFile("file", request.file); + _request.append("fileName", request.fileName); + if (request.publicKey != null) { + _request.append("publicKey", request.publicKey); + } + + if (request.signature != null) { + _request.append("signature", request.signature); + } + + if (request.expire != null) { + _request.append("expire", request.expire.toString()); + } + + if (request.token != null) { + _request.append("token", request.token); + } + + if (request.useUniqueFileName != null) { + _request.append("useUniqueFileName", request.useUniqueFileName.toString()); + } + + if (request.tags != null) { + for (const _item of request.tags) { + _request.append("tags", _item); + } + } + + if (request.folder != null) { + _request.append("folder", request.folder); + } + + if (request.isPrivateFile != null) { + _request.append("isPrivateFile", request.isPrivateFile.toString()); + } + + if (request.isPublished != null) { + _request.append("isPublished", request.isPublished.toString()); + } + + if (request.customCoordinates != null) { + _request.append("customCoordinates", request.customCoordinates); + } + + if (request.responseFields != null) { + for (const _item of request.responseFields) { + _request.append("responseFields", _item); + } + } + + if (request.extensions != null) { + for (const _item of request.extensions) { + _request.append("extensions", typeof _item === "string" ? _item : toJson(_item)); + } + } + + if (request.webhookUrl != null) { + _request.append("webhookUrl", request.webhookUrl); + } + + if (request.overwriteFile != null) { + _request.append("overwriteFile", request.overwriteFile.toString()); + } + + if (request.overwriteAITags != null) { + _request.append("overwriteAITags", request.overwriteAITags.toString()); + } + + if (request.overwriteTags != null) { + _request.append("overwriteTags", request.overwriteTags.toString()); + } + + if (request.overwriteCustomMetadata != null) { + _request.append("overwriteCustomMetadata", request.overwriteCustomMetadata.toString()); + } + + if (request.customMetadata != null) { + _request.append("customMetadata", toJson(request.customMetadata)); + } + + if (request.transformation != null) { + _request.append("transformation", toJson(request.transformation)); + } + + if (request.checks != null) { + _request.append("checks", request.checks); + } + + if (request.description != null) { + _request.append("description", request.description); + } + + const _maybeEncodedRequest = await _request.getRequest(); + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ + Authorization: await this._getAuthorizationHeader(), + ..._maybeEncodedRequest.headers, + }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "api/v1/files/upload", + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "file", + duplex: _maybeEncodedRequest.duplex, + body: _maybeEncodedRequest.body, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.Upload, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /api/v1/files/upload."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API returns an object with details or attributes about the current version of the file. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in the list and search assets API and upload API. + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.get("fileId") + */ + public get(fileId: string, requestOptions?: Files.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(fileId, requestOptions)); + } + + private async __get( + fileId: string, + requestOptions?: Files.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/details`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FileDetails, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files/{fileId}/details."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {ImageKit.FilesUpdateRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.update("fileId", { + * removeAITags: ["car", "vehicle", "motorsports"], + * webhookUrl: "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + * 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" + * }], + * tags: ["tag1", "tag2"], + * customCoordinates: "10,10,100,100", + * customMetadata: { + * "brand": "Nike", + * "color": "red" + * } + * }) + */ + public update( + fileId: string, + request: ImageKit.FilesUpdateRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(fileId, request, requestOptions)); + } + + private async __update( + fileId: string, + request: ImageKit.FilesUpdateRequest, + requestOptions?: Files.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/details`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FilesUpdateResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling PATCH /v1/files/{fileId}/details.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.delete("fileId") + */ + public delete(fileId: string, requestOptions?: Files.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(fileId, requestOptions)); + } + + private async __delete(fileId: string, requestOptions?: Files.RequestOptions): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}`, + ), + method: "DELETE", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling DELETE /v1/files/{fileId}."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FilesCopyRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.copy({ + * sourceFilePath: "/path/to/file.jpg", + * destinationPath: "/folder/to/copy/into/" + * }) + */ + public copy( + request: ImageKit.FilesCopyRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__copy(request, requestOptions)); + } + + private async __copy( + request: ImageKit.FilesCopyRequest, + requestOptions?: Files.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/copy", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/copy."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FilesMoveRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.move({ + * sourceFilePath: "/path/to/file.jpg", + * destinationPath: "/folder/to/move/into/" + * }) + */ + public move( + request: ImageKit.FilesMoveRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__move(request, requestOptions)); + } + + private async __move( + request: ImageKit.FilesMoveRequest, + requestOptions?: Files.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/move", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/move."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FilesRenameRequest} request + * @param {Files.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.ConflictError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.rename({ + * filePath: "/path/to/file.jpg", + * newFileName: "newFileName.jpg" + * }) + */ + public rename( + request: ImageKit.FilesRenameRequest, + requestOptions?: Files.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__rename(request, requestOptions)); + } + + private async __rename( + request: ImageKit.FilesRenameRequest, + requestOptions?: Files.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/rename", + ), + method: "PUT", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FilesRenameResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new ImageKit.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling PUT /v1/files/rename."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/files/client/index.ts b/src/api/resources/files/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/files/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/files/client/requests/FileUploadV1.ts b/src/api/resources/files/client/requests/FileUploadV1.ts new file mode 100644 index 00000000..c91b00ef --- /dev/null +++ b/src/api/resources/files/client/requests/FileUploadV1.ts @@ -0,0 +1,169 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as fs from "fs"; +import * as core from "../../../../../core/index.js"; +import * as ImageKit from "../../../../index.js"; + +/** + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + * + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + * + * @example + * { + * file: fs.createReadStream("/path/to/your/file"), + * fileName: "fileName" + * } + */ +export interface FileUploadV1 { + /** + * 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: core.file.Uploadable.FileLike; + /** + * 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; + /** Your ImageKit.io public key. This field is only required for authentication when uploading a file from the client side. */ + publicKey?: string; + /** + * 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; + /** 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; + /** + * 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; + /** + * 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; + /** + * 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?: string[]; + /** + * 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; + /** + * 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; + /** Array of response field keys to include in the API response body. */ + responseFields?: FileUploadV1.ResponseFields.Item[]; + /** Array of extensions to be applied to the image. Each extension can be configured with specific parameters based on the extension type. */ + extensions?: FileUploadV1.Extensions.Item[]; + /** 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; + /** 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 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 `tags`, and a file already exists at the exact location, existing tags will be removed. */ + overwriteTags?: boolean; + /** If the request does not have `customMetadata`, and a file already exists at the exact location, existing customMetadata will be removed. */ + overwriteCustomMetadata?: boolean; + /** JSON key-value pairs to associate with the asset. Create the custom metadata fields before setting these values. */ + customMetadata?: Record; + transformation?: ImageKit.TransformationObject; + /** + * 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; + /** Optional text to describe the contents of the file. */ + description?: string; +} + +export namespace FileUploadV1 { + export type ResponseFields = ResponseFields.Item[]; + + export namespace ResponseFields { + export type Item = + | "tags" + | "customCoordinates" + | "isPrivateFile" + | "embeddedMetadata" + | "isPublished" + | "customMetadata" + | "metadata"; + export const Item = { + Tags: "tags", + CustomCoordinates: "customCoordinates", + IsPrivateFile: "isPrivateFile", + EmbeddedMetadata: "embeddedMetadata", + IsPublished: "isPublished", + CustomMetadata: "customMetadata", + Metadata: "metadata", + } as const; + } + + export type Extensions = Extensions.Item[]; + + export namespace Extensions { + export type Item = + | ImageKit.RemovedotBgExtension + | ImageKit.AutoTaggingExtension + | ImageKit.AutoDescriptionExtension; + } +} diff --git a/src/api/resources/files/client/requests/FilesCopyRequest.ts b/src/api/resources/files/client/requests/FilesCopyRequest.ts new file mode 100644 index 00000000..d311fc33 --- /dev/null +++ b/src/api/resources/files/client/requests/FilesCopyRequest.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * sourceFilePath: "/path/to/file.jpg", + * destinationPath: "/folder/to/copy/into/" + * } + */ +export interface FilesCopyRequest { + /** The full path of the file you want to copy. */ + sourceFilePath: string; + /** Full path to the folder you want to copy the above file 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 - `false`. */ + includeFileVersions?: boolean; +} diff --git a/src/api/resources/files/client/requests/FilesMoveRequest.ts b/src/api/resources/files/client/requests/FilesMoveRequest.ts new file mode 100644 index 00000000..45a2bcad --- /dev/null +++ b/src/api/resources/files/client/requests/FilesMoveRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * sourceFilePath: "/path/to/file.jpg", + * destinationPath: "/folder/to/move/into/" + * } + */ +export interface FilesMoveRequest { + /** The full path of the file you want to move. */ + sourceFilePath: string; + /** Full path to the folder you want to move the above file into. */ + destinationPath: string; +} diff --git a/src/api/resources/files/client/requests/FilesRenameRequest.ts b/src/api/resources/files/client/requests/FilesRenameRequest.ts new file mode 100644 index 00000000..87d804a0 --- /dev/null +++ b/src/api/resources/files/client/requests/FilesRenameRequest.ts @@ -0,0 +1,36 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * filePath: "/path/to/file.jpg", + * newFileName: "newFileName.jpg" + * } + */ +export interface FilesRenameRequest { + /** 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; +} diff --git a/src/api/resources/files/client/requests/index.ts b/src/api/resources/files/client/requests/index.ts new file mode 100644 index 00000000..e3c5670f --- /dev/null +++ b/src/api/resources/files/client/requests/index.ts @@ -0,0 +1,4 @@ +export { type FileUploadV1 } from "./FileUploadV1.js"; +export { type FilesCopyRequest } from "./FilesCopyRequest.js"; +export { type FilesMoveRequest } from "./FilesMoveRequest.js"; +export { type FilesRenameRequest } from "./FilesRenameRequest.js"; diff --git a/src/api/resources/files/index.ts b/src/api/resources/files/index.ts new file mode 100644 index 00000000..751123a2 --- /dev/null +++ b/src/api/resources/files/index.ts @@ -0,0 +1,3 @@ +export * from "./types/index.js"; +export * from "./resources/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/files/resources/bulk/client/Client.ts b/src/api/resources/files/resources/bulk/client/Client.ts new file mode 100644 index 00000000..6220ec21 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/Client.ts @@ -0,0 +1,432 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Bulk { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Bulk { + protected readonly _options: Bulk.Options; + + constructor(_options: Bulk.Options) { + this._options = _options; + } + + /** + * 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. + * + * @param {ImageKit.files.BulkDeleteRequest} request + * @param {Bulk.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.bulk.delete({ + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"] + * }) + */ + public delete( + request: ImageKit.files.BulkDeleteRequest, + requestOptions?: Bulk.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(request, requestOptions)); + } + + private async __delete( + request: ImageKit.files.BulkDeleteRequest, + requestOptions?: Bulk.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/batch/deleteByFileIds", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.files.BulkDeleteResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling POST /v1/files/batch/deleteByFileIds.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API adds tags to multiple files in bulk. A maximum of 50 files can be specified at a time. + * + * @param {ImageKit.files.BulkAddTagsRequest} request + * @param {Bulk.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.bulk.addTags({ + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * tags: ["t-shirt", "round-neck", "sale2019"] + * }) + */ + public addTags( + request: ImageKit.files.BulkAddTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__addTags(request, requestOptions)); + } + + private async __addTags( + request: ImageKit.files.BulkAddTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/addTags", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.files.BulkAddTagsResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/addTags."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API removes tags from multiple files in bulk. A maximum of 50 files can be specified at a time. + * + * @param {ImageKit.files.BulkRemoveTagsRequest} request + * @param {Bulk.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.bulk.removeTags({ + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * tags: ["t-shirt", "round-neck", "sale2019"] + * }) + */ + public removeTags( + request: ImageKit.files.BulkRemoveTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__removeTags(request, requestOptions)); + } + + private async __removeTags( + request: ImageKit.files.BulkRemoveTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/removeTags", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + data: _response.body as ImageKit.files.BulkRemoveTagsResponse, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/removeTags."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API removes AITags from multiple files in bulk. A maximum of 50 files can be specified at a time. + * + * @param {ImageKit.files.BulkRemoveAiTagsRequest} request + * @param {Bulk.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.bulk.removeAiTags({ + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * AITags: ["t-shirt", "round-neck", "sale2019"] + * }) + */ + public removeAiTags( + request: ImageKit.files.BulkRemoveAiTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__removeAiTags(request, requestOptions)); + } + + private async __removeAiTags( + request: ImageKit.files.BulkRemoveAiTagsRequest, + requestOptions?: Bulk.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/removeAITags", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + data: _response.body as ImageKit.files.BulkRemoveAiTagsResponse, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/files/removeAITags."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/files/resources/bulk/client/index.ts b/src/api/resources/files/resources/bulk/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/files/resources/bulk/client/requests/BulkAddTagsRequest.ts b/src/api/resources/files/resources/bulk/client/requests/BulkAddTagsRequest.ts new file mode 100644 index 00000000..7c4351d9 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/requests/BulkAddTagsRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * tags: ["t-shirt", "round-neck", "sale2019"] + * } + */ +export interface BulkAddTagsRequest { + /** An array of fileIds to which you want to add tags. */ + fileIds: string[]; + /** An array of tags that you want to add to the files. */ + tags: string[]; +} diff --git a/src/api/resources/files/resources/bulk/client/requests/BulkDeleteRequest.ts b/src/api/resources/files/resources/bulk/client/requests/BulkDeleteRequest.ts new file mode 100644 index 00000000..f952a2a0 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/requests/BulkDeleteRequest.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"] + * } + */ +export interface BulkDeleteRequest { + /** An array of fileIds which you want to delete. */ + fileIds: string[]; +} diff --git a/src/api/resources/files/resources/bulk/client/requests/BulkRemoveAiTagsRequest.ts b/src/api/resources/files/resources/bulk/client/requests/BulkRemoveAiTagsRequest.ts new file mode 100644 index 00000000..2e05f836 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/requests/BulkRemoveAiTagsRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * AITags: ["t-shirt", "round-neck", "sale2019"] + * } + */ +export interface BulkRemoveAiTagsRequest { + /** An array of fileIds from which you want to remove AITags. */ + fileIds: string[]; + /** An array of AITags that you want to remove from the files. */ + AITags: string[]; +} diff --git a/src/api/resources/files/resources/bulk/client/requests/BulkRemoveTagsRequest.ts b/src/api/resources/files/resources/bulk/client/requests/BulkRemoveTagsRequest.ts new file mode 100644 index 00000000..5af6eb77 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/requests/BulkRemoveTagsRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + * tags: ["t-shirt", "round-neck", "sale2019"] + * } + */ +export interface BulkRemoveTagsRequest { + /** An array of fileIds from which you want to remove tags. */ + fileIds: string[]; + /** An array of tags that you want to remove from the files. */ + tags: string[]; +} diff --git a/src/api/resources/files/resources/bulk/client/requests/index.ts b/src/api/resources/files/resources/bulk/client/requests/index.ts new file mode 100644 index 00000000..7243f995 --- /dev/null +++ b/src/api/resources/files/resources/bulk/client/requests/index.ts @@ -0,0 +1,4 @@ +export { type BulkDeleteRequest } from "./BulkDeleteRequest.js"; +export { type BulkAddTagsRequest } from "./BulkAddTagsRequest.js"; +export { type BulkRemoveTagsRequest } from "./BulkRemoveTagsRequest.js"; +export { type BulkRemoveAiTagsRequest } from "./BulkRemoveAiTagsRequest.js"; diff --git a/src/api/resources/files/resources/bulk/index.ts b/src/api/resources/files/resources/bulk/index.ts new file mode 100644 index 00000000..f095e147 --- /dev/null +++ b/src/api/resources/files/resources/bulk/index.ts @@ -0,0 +1,2 @@ +export * from "./types/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/files/resources/bulk/types/BulkAddTagsResponse.ts b/src/api/resources/files/resources/bulk/types/BulkAddTagsResponse.ts new file mode 100644 index 00000000..b3baa2f6 --- /dev/null +++ b/src/api/resources/files/resources/bulk/types/BulkAddTagsResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface BulkAddTagsResponse { + /** An array of fileIds that in which tags were successfully added. */ + successfullyUpdatedFileIds?: string[]; +} diff --git a/src/api/resources/files/resources/bulk/types/BulkDeleteResponse.ts b/src/api/resources/files/resources/bulk/types/BulkDeleteResponse.ts new file mode 100644 index 00000000..1b38a44f --- /dev/null +++ b/src/api/resources/files/resources/bulk/types/BulkDeleteResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface BulkDeleteResponse { + /** An array of fileIds that were successfully deleted. */ + successfullyDeletedFileIds?: string[]; +} diff --git a/src/api/resources/files/resources/bulk/types/BulkRemoveAiTagsResponse.ts b/src/api/resources/files/resources/bulk/types/BulkRemoveAiTagsResponse.ts new file mode 100644 index 00000000..3c7029c2 --- /dev/null +++ b/src/api/resources/files/resources/bulk/types/BulkRemoveAiTagsResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface BulkRemoveAiTagsResponse { + /** An array of fileIds that in which AITags were successfully removed. */ + successfullyUpdatedFileIds?: string[]; +} diff --git a/src/api/resources/files/resources/bulk/types/BulkRemoveTagsResponse.ts b/src/api/resources/files/resources/bulk/types/BulkRemoveTagsResponse.ts new file mode 100644 index 00000000..e62d1509 --- /dev/null +++ b/src/api/resources/files/resources/bulk/types/BulkRemoveTagsResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface BulkRemoveTagsResponse { + /** An array of fileIds that in which tags were successfully removed. */ + successfullyUpdatedFileIds?: string[]; +} diff --git a/src/api/resources/files/resources/bulk/types/index.ts b/src/api/resources/files/resources/bulk/types/index.ts new file mode 100644 index 00000000..c9ef2d51 --- /dev/null +++ b/src/api/resources/files/resources/bulk/types/index.ts @@ -0,0 +1,4 @@ +export * from "./BulkDeleteResponse.js"; +export * from "./BulkAddTagsResponse.js"; +export * from "./BulkRemoveTagsResponse.js"; +export * from "./BulkRemoveAiTagsResponse.js"; diff --git a/src/api/resources/files/resources/index.ts b/src/api/resources/files/resources/index.ts new file mode 100644 index 00000000..b649a0ed --- /dev/null +++ b/src/api/resources/files/resources/index.ts @@ -0,0 +1,6 @@ +export * as bulk from "./bulk/index.js"; +export * from "./bulk/types/index.js"; +export * as versions from "./versions/index.js"; +export * as metadata from "./metadata/index.js"; +export * from "./bulk/client/requests/index.js"; +export * from "./metadata/client/requests/index.js"; diff --git a/src/api/resources/files/resources/metadata/client/Client.ts b/src/api/resources/files/resources/metadata/client/Client.ts new file mode 100644 index 00000000..041a5da6 --- /dev/null +++ b/src/api/resources/files/resources/metadata/client/Client.ts @@ -0,0 +1,227 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Metadata { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Metadata { + protected readonly _options: Metadata.Options; + + constructor(_options: Metadata.Options) { + this._options = _options; + } + + /** + * 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. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in the list and search assets API and upload API. + * @param {Metadata.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.metadata.get("fileId") + */ + public get(fileId: string, requestOptions?: Metadata.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(fileId, requestOptions)); + } + + private async __get( + fileId: string, + requestOptions?: Metadata.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/metadata`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.Metadata, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files/{fileId}/metadata."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * Get image EXIF, pHash, and other metadata from ImageKit.io powered remote URL using this API. + * + * @param {ImageKit.files.MetadataGetFromUrlRequest} request + * @param {Metadata.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.metadata.getFromUrl({ + * url: "url" + * }) + */ + public getFromUrl( + request: ImageKit.files.MetadataGetFromUrlRequest, + requestOptions?: Metadata.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__getFromUrl(request, requestOptions)); + } + + private async __getFromUrl( + request: ImageKit.files.MetadataGetFromUrlRequest, + requestOptions?: Metadata.RequestOptions, + ): Promise> { + const { url } = request; + const _queryParams: Record = {}; + _queryParams["url"] = url; + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/files/metadata", + ), + method: "GET", + headers: _headers, + queryParameters: { ..._queryParams, ...requestOptions?.queryParams }, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.Metadata, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files/metadata."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/files/resources/metadata/client/index.ts b/src/api/resources/files/resources/metadata/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/files/resources/metadata/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/files/resources/metadata/client/requests/MetadataGetFromUrlRequest.ts b/src/api/resources/files/resources/metadata/client/requests/MetadataGetFromUrlRequest.ts new file mode 100644 index 00000000..a0d6a0ef --- /dev/null +++ b/src/api/resources/files/resources/metadata/client/requests/MetadataGetFromUrlRequest.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * url: "url" + * } + * + * @example + * { + * url: "url" + * } + */ +export interface MetadataGetFromUrlRequest { + /** Should be a valid file URL. It should be accessible using your ImageKit.io account. */ + url: string; +} diff --git a/src/api/resources/files/resources/metadata/client/requests/index.ts b/src/api/resources/files/resources/metadata/client/requests/index.ts new file mode 100644 index 00000000..48f7e0de --- /dev/null +++ b/src/api/resources/files/resources/metadata/client/requests/index.ts @@ -0,0 +1 @@ +export { type MetadataGetFromUrlRequest } from "./MetadataGetFromUrlRequest.js"; diff --git a/src/api/resources/files/resources/metadata/index.ts b/src/api/resources/files/resources/metadata/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/files/resources/metadata/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/files/resources/versions/client/Client.ts b/src/api/resources/files/resources/versions/client/Client.ts new file mode 100644 index 00000000..c124c166 --- /dev/null +++ b/src/api/resources/files/resources/versions/client/Client.ts @@ -0,0 +1,414 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Versions { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Versions { + protected readonly _options: Versions.Options; + + constructor(_options: Versions.Options) { + this._options = _options; + } + + /** + * This API returns details of all versions of a file. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {Versions.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.versions.list("fileId") + */ + public list( + fileId: string, + requestOptions?: Versions.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(fileId, requestOptions)); + } + + private async __list( + fileId: string, + requestOptions?: Versions.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/versions`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FileDetails[], rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/files/{fileId}/versions."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API returns an object with details or attributes of a file version. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {string} versionId - The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + * @param {Versions.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.versions.get("fileId", "versionId") + */ + public get( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(fileId, versionId, requestOptions)); + } + + private async __get( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/versions/${encodeURIComponent(versionId)}`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FileDetails, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling GET /v1/files/{fileId}/versions/{versionId}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {string} versionId - The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + * @param {Versions.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.versions.delete("fileId", "versionId") + */ + public delete( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__delete(fileId, versionId, requestOptions)); + } + + private async __delete( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/versions/${encodeURIComponent(versionId)}`, + ), + method: "DELETE", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling DELETE /v1/files/{fileId}/versions/{versionId}.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This API restores a file version as the current file version. + * + * @param {string} fileId - The unique `fileId` of the uploaded file. `fileId` is returned in list and search assets API and upload API. + * @param {string} versionId - The unique `versionId` of the uploaded file. `versionId` is returned in list and search assets API and upload API. + * @param {Versions.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.files.versions.restore("fileId", "versionId") + */ + public restore( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__restore(fileId, versionId, requestOptions)); + } + + private async __restore( + fileId: string, + versionId: string, + requestOptions?: Versions.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/files/${encodeURIComponent(fileId)}/versions/${encodeURIComponent(versionId)}/restore`, + ), + method: "PUT", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FileDetails, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError( + "Timeout exceeded when calling PUT /v1/files/{fileId}/versions/{versionId}/restore.", + ); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/files/resources/versions/client/index.ts b/src/api/resources/files/resources/versions/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/files/resources/versions/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/files/resources/versions/index.ts b/src/api/resources/files/resources/versions/index.ts new file mode 100644 index 00000000..914b8c3c --- /dev/null +++ b/src/api/resources/files/resources/versions/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/api/resources/files/types/FilesRenameResponse.ts b/src/api/resources/files/types/FilesRenameResponse.ts new file mode 100644 index 00000000..a6fd1e61 --- /dev/null +++ b/src/api/resources/files/types/FilesRenameResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FilesRenameResponse { + /** Unique identifier of the purge request. This can be used to check the status of the purge request. */ + purgeRequestId?: string; +} diff --git a/src/api/resources/files/types/FilesUpdateRequest.ts b/src/api/resources/files/types/FilesUpdateRequest.ts new file mode 100644 index 00000000..caa9b3f2 --- /dev/null +++ b/src/api/resources/files/types/FilesUpdateRequest.ts @@ -0,0 +1,26 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../../../index.js"; + +export type FilesUpdateRequest = + | { + removeAITags?: (string[] | "all") | undefined; + webhookUrl?: string | undefined; + extensions?: + | (ImageKit.RemovedotBgExtension | ImageKit.AutoTaggingExtension | ImageKit.AutoDescriptionExtension)[] + | undefined; + tags?: string[] | undefined; + customCoordinates?: string | undefined; + customMetadata?: Record | undefined; + description?: string | undefined; + } + | { + publish?: + | { + isPublished: boolean; + includeFileVersions?: boolean | undefined; + } + | undefined; + }; diff --git a/src/api/resources/files/types/FilesUpdateResponse.ts b/src/api/resources/files/types/FilesUpdateResponse.ts new file mode 100644 index 00000000..ab0a0740 --- /dev/null +++ b/src/api/resources/files/types/FilesUpdateResponse.ts @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../../../index.js"; + +export interface FilesUpdateResponse extends ImageKit.FileDetails { + extensionStatus?: FilesUpdateResponse.ExtensionStatus; +} + +export namespace FilesUpdateResponse { + export interface ExtensionStatus { + "google-auto-tagging"?: ExtensionStatus.GoogleAutoTagging; + "aws-auto-tagging"?: ExtensionStatus.AwsAutoTagging; + "remove-bg"?: ExtensionStatus.RemoveBg; + "ai-auto-description"?: ExtensionStatus.AiAutoDescription; + } + + export namespace ExtensionStatus { + export type GoogleAutoTagging = "success" | "pending" | "failed"; + export const GoogleAutoTagging = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + export type AwsAutoTagging = "success" | "pending" | "failed"; + export const AwsAutoTagging = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + export type RemoveBg = "success" | "pending" | "failed"; + export const RemoveBg = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + export type AiAutoDescription = "success" | "pending" | "failed"; + export const AiAutoDescription = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + } +} diff --git a/src/api/resources/files/types/index.ts b/src/api/resources/files/types/index.ts new file mode 100644 index 00000000..883aa6ed --- /dev/null +++ b/src/api/resources/files/types/index.ts @@ -0,0 +1,3 @@ +export * from "./FilesUpdateRequest.js"; +export * from "./FilesUpdateResponse.js"; +export * from "./FilesRenameResponse.js"; diff --git a/src/api/resources/folders/client/Client.ts b/src/api/resources/folders/client/Client.ts new file mode 100644 index 00000000..cafb38cb --- /dev/null +++ b/src/api/resources/folders/client/Client.ts @@ -0,0 +1,528 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../environments.js"; +import * as core from "../../../../core/index.js"; +import * as ImageKit from "../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js"; +import * as errors from "../../../../errors/index.js"; +import { Job } from "../resources/job/client/Client.js"; + +export declare namespace Folders { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Folders { + protected readonly _options: Folders.Options; + protected _job: Job | undefined; + + constructor(_options: Folders.Options) { + this._options = _options; + } + + public get job(): Job { + return (this._job ??= new Job(this._options)); + } + + /** + * 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. + * + * @param {ImageKit.FoldersCreateRequest} request + * @param {Folders.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.create({ + * folderName: "summer", + * parentFolderPath: "/product/images/" + * }) + */ + public create( + request: ImageKit.FoldersCreateRequest, + requestOptions?: Folders.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: ImageKit.FoldersCreateRequest, + requestOptions?: Folders.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/folder", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/folder."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * This will delete a folder and all its contents permanently. The API returns an empty response. + * + * @param {ImageKit.FoldersDeleteRequest} request + * @param {Folders.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.delete({ + * folderPath: "/folder/to/delete/" + * }) + */ + public delete( + request: ImageKit.FoldersDeleteRequest, + requestOptions?: Folders.RequestOptions, + ): core.HttpResponsePromise> { + return core.HttpResponsePromise.fromPromise(this.__delete(request, requestOptions)); + } + + private async __delete( + request: ImageKit.FoldersDeleteRequest, + requestOptions?: Folders.RequestOptions, + ): Promise>> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/folder", + ), + method: "DELETE", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as Record, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling DELETE /v1/folder."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FoldersCopyRequest} request + * @param {Folders.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.LockedError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.copy({ + * sourceFolderPath: "/path/of/source/folder", + * destinationPath: "/path/of/destination/folder" + * }) + */ + public copy( + request: ImageKit.FoldersCopyRequest, + requestOptions?: Folders.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__copy(request, requestOptions)); + } + + private async __copy( + request: ImageKit.FoldersCopyRequest, + requestOptions?: Folders.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/bulkJobs/copyFolder", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FoldersCopyResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 423: + throw new ImageKit.LockedError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/bulkJobs/copyFolder."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FoldersMoveRequest} request + * @param {Folders.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.LockedError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.move({ + * sourceFolderPath: "/path/of/source/folder", + * destinationPath: "/path/of/destination/folder" + * }) + */ + public move( + request: ImageKit.FoldersMoveRequest, + requestOptions?: Folders.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__move(request, requestOptions)); + } + + private async __move( + request: ImageKit.FoldersMoveRequest, + requestOptions?: Folders.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/bulkJobs/moveFolder", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FoldersMoveResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 423: + throw new ImageKit.LockedError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/bulkJobs/moveFolder."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + /** + * 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. + * + * @param {ImageKit.FoldersRenameRequest} request + * @param {Folders.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.BadRequestError} + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.ConflictError} + * @throws {@link ImageKit.LockedError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.rename({ + * folderPath: "/path/of/folder", + * newFolderName: "new-folder-name" + * }) + */ + public rename( + request: ImageKit.FoldersRenameRequest, + requestOptions?: Folders.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__rename(request, requestOptions)); + } + + private async __rename( + request: ImageKit.FoldersRenameRequest, + requestOptions?: Folders.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + "v1/bulkJobs/renameFolder", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryParameters: requestOptions?.queryParams, + requestType: "json", + body: request, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.FoldersRenameResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new ImageKit.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new ImageKit.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 423: + throw new ImageKit.LockedError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling POST /v1/bulkJobs/renameFolder."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/folders/client/index.ts b/src/api/resources/folders/client/index.ts new file mode 100644 index 00000000..82648c6f --- /dev/null +++ b/src/api/resources/folders/client/index.ts @@ -0,0 +1,2 @@ +export {}; +export * from "./requests/index.js"; diff --git a/src/api/resources/folders/client/requests/FoldersCopyRequest.ts b/src/api/resources/folders/client/requests/FoldersCopyRequest.ts new file mode 100644 index 00000000..600aad81 --- /dev/null +++ b/src/api/resources/folders/client/requests/FoldersCopyRequest.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * sourceFolderPath: "/path/of/source/folder", + * destinationPath: "/path/of/destination/folder" + * } + */ +export interface FoldersCopyRequest { + /** The full path to the source folder you want to copy. */ + sourceFolderPath: string; + /** Full path to the destination folder where you want to copy the source folder into. */ + 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`. */ + includeVersions?: boolean; +} diff --git a/src/api/resources/folders/client/requests/FoldersCreateRequest.ts b/src/api/resources/folders/client/requests/FoldersCreateRequest.ts new file mode 100644 index 00000000..7f439dd7 --- /dev/null +++ b/src/api/resources/folders/client/requests/FoldersCreateRequest.ts @@ -0,0 +1,25 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * folderName: "summer", + * parentFolderPath: "/product/images/" + * } + */ +export interface FoldersCreateRequest { + /** + * 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; +} diff --git a/src/api/resources/folders/client/requests/FoldersDeleteRequest.ts b/src/api/resources/folders/client/requests/FoldersDeleteRequest.ts new file mode 100644 index 00000000..ad23ed89 --- /dev/null +++ b/src/api/resources/folders/client/requests/FoldersDeleteRequest.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * folderPath: "/folder/to/delete/" + * } + */ +export interface FoldersDeleteRequest { + /** Full path to the folder you want to delete. For example `/folder/to/delete/`. */ + folderPath: string; +} diff --git a/src/api/resources/folders/client/requests/FoldersMoveRequest.ts b/src/api/resources/folders/client/requests/FoldersMoveRequest.ts new file mode 100644 index 00000000..b2712323 --- /dev/null +++ b/src/api/resources/folders/client/requests/FoldersMoveRequest.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * sourceFolderPath: "/path/of/source/folder", + * destinationPath: "/path/of/destination/folder" + * } + */ +export interface FoldersMoveRequest { + /** The full path to the source folder you want to move. */ + sourceFolderPath: string; + /** Full path to the destination folder where you want to move the source folder into. */ + destinationPath: string; +} diff --git a/src/api/resources/folders/client/requests/FoldersRenameRequest.ts b/src/api/resources/folders/client/requests/FoldersRenameRequest.ts new file mode 100644 index 00000000..89ebb074 --- /dev/null +++ b/src/api/resources/folders/client/requests/FoldersRenameRequest.ts @@ -0,0 +1,31 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * @example + * { + * folderPath: "/path/of/folder", + * newFolderName: "new-folder-name" + * } + */ +export interface FoldersRenameRequest { + /** 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; +} diff --git a/src/api/resources/folders/client/requests/index.ts b/src/api/resources/folders/client/requests/index.ts new file mode 100644 index 00000000..51fea4db --- /dev/null +++ b/src/api/resources/folders/client/requests/index.ts @@ -0,0 +1,5 @@ +export { type FoldersCreateRequest } from "./FoldersCreateRequest.js"; +export { type FoldersDeleteRequest } from "./FoldersDeleteRequest.js"; +export { type FoldersCopyRequest } from "./FoldersCopyRequest.js"; +export { type FoldersMoveRequest } from "./FoldersMoveRequest.js"; +export { type FoldersRenameRequest } from "./FoldersRenameRequest.js"; diff --git a/src/api/resources/folders/index.ts b/src/api/resources/folders/index.ts new file mode 100644 index 00000000..751123a2 --- /dev/null +++ b/src/api/resources/folders/index.ts @@ -0,0 +1,3 @@ +export * from "./types/index.js"; +export * from "./resources/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/folders/resources/index.ts b/src/api/resources/folders/resources/index.ts new file mode 100644 index 00000000..23d6b38f --- /dev/null +++ b/src/api/resources/folders/resources/index.ts @@ -0,0 +1,2 @@ +export * as job from "./job/index.js"; +export * from "./job/types/index.js"; diff --git a/src/api/resources/folders/resources/job/client/Client.ts b/src/api/resources/folders/resources/job/client/Client.ts new file mode 100644 index 00000000..f9e597af --- /dev/null +++ b/src/api/resources/folders/resources/job/client/Client.ts @@ -0,0 +1,133 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as environments from "../../../../../../environments.js"; +import * as core from "../../../../../../core/index.js"; +import * as ImageKit from "../../../../../index.js"; +import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import * as errors from "../../../../../../errors/index.js"; + +export declare namespace Job { + export interface Options { + environment?: core.Supplier; + /** Specify a custom URL to connect the client to. */ + baseUrl?: core.Supplier; + username: core.Supplier; + password: core.Supplier; + /** Additional headers to include in requests. */ + headers?: Record | undefined>; + } + + export interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; + /** Additional headers to include in the request. */ + headers?: Record | undefined>; + } +} + +export class Job { + protected readonly _options: Job.Options; + + constructor(_options: Job.Options) { + this._options = _options; + } + + /** + * This API returns the status of a bulk job like copy and move folder operations. + * + * @param {string} jobId - The `jobId` is returned in the response of bulk job API e.g. copy folder or move folder API. + * @param {Job.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link ImageKit.UnauthorizedError} + * @throws {@link ImageKit.ForbiddenError} + * @throws {@link ImageKit.NotFoundError} + * @throws {@link ImageKit.TooManyRequestsError} + * + * @example + * await client.folders.job.get("jobId") + */ + public get( + jobId: string, + requestOptions?: Job.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(jobId, requestOptions)); + } + + private async __get( + jobId: string, + requestOptions?: Job.RequestOptions, + ): Promise> { + var _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ImageKitEnvironment.Default, + `v1/bulkJobs/${encodeURIComponent(jobId)}`, + ), + method: "GET", + headers: _headers, + queryParameters: requestOptions?.queryParams, + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { data: _response.body as ImageKit.folders.JobGetResponse, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new ImageKit.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new ImageKit.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new ImageKit.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new ImageKit.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.ImageKitError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + rawResponse: _response.rawResponse, + }); + case "timeout": + throw new errors.ImageKitTimeoutError("Timeout exceeded when calling GET /v1/bulkJobs/{jobId}."); + case "unknown": + throw new errors.ImageKitError({ + message: _response.error.errorMessage, + rawResponse: _response.rawResponse, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + return core.BasicAuth.toAuthorizationHeader({ + username: await core.Supplier.get(this._options.username), + password: await core.Supplier.get(this._options.password), + }); + } +} diff --git a/src/api/resources/folders/resources/job/client/index.ts b/src/api/resources/folders/resources/job/client/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/api/resources/folders/resources/job/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/api/resources/folders/resources/job/index.ts b/src/api/resources/folders/resources/job/index.ts new file mode 100644 index 00000000..f095e147 --- /dev/null +++ b/src/api/resources/folders/resources/job/index.ts @@ -0,0 +1,2 @@ +export * from "./types/index.js"; +export * from "./client/index.js"; diff --git a/src/api/resources/folders/resources/job/types/JobGetResponse.ts b/src/api/resources/folders/resources/job/types/JobGetResponse.ts new file mode 100644 index 00000000..e7bd195b --- /dev/null +++ b/src/api/resources/folders/resources/job/types/JobGetResponse.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface JobGetResponse { + /** Unique identifier of the bulk job. */ + jobId?: string; + /** Type of the bulk job. Possible values - `COPY_FOLDER`, `MOVE_FOLDER`, `RENAME_FOLDER`. */ + type?: string; + /** Status of the bulk job. Possible values - `Pending`, `Completed`. */ + status?: 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; +} diff --git a/src/api/resources/folders/resources/job/types/index.ts b/src/api/resources/folders/resources/job/types/index.ts new file mode 100644 index 00000000..f7b4caf1 --- /dev/null +++ b/src/api/resources/folders/resources/job/types/index.ts @@ -0,0 +1 @@ +export * from "./JobGetResponse.js"; diff --git a/src/api/resources/folders/types/FoldersCopyResponse.ts b/src/api/resources/folders/types/FoldersCopyResponse.ts new file mode 100644 index 00000000..5bc2a2a9 --- /dev/null +++ b/src/api/resources/folders/types/FoldersCopyResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FoldersCopyResponse { + /** Unique identifier of the bulk job. This can be used to check the status of the bulk job. */ + jobId?: string; +} diff --git a/src/api/resources/folders/types/FoldersMoveResponse.ts b/src/api/resources/folders/types/FoldersMoveResponse.ts new file mode 100644 index 00000000..b8fe167f --- /dev/null +++ b/src/api/resources/folders/types/FoldersMoveResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FoldersMoveResponse { + /** Unique identifier of the bulk job. This can be used to check the status of the bulk job. */ + jobId?: string; +} diff --git a/src/api/resources/folders/types/FoldersRenameResponse.ts b/src/api/resources/folders/types/FoldersRenameResponse.ts new file mode 100644 index 00000000..aa875375 --- /dev/null +++ b/src/api/resources/folders/types/FoldersRenameResponse.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface FoldersRenameResponse { + /** Unique identifier of the bulk job. This can be used to check the status of the bulk job. */ + jobId?: string; +} diff --git a/src/api/resources/folders/types/index.ts b/src/api/resources/folders/types/index.ts new file mode 100644 index 00000000..a7ff5530 --- /dev/null +++ b/src/api/resources/folders/types/index.ts @@ -0,0 +1,3 @@ +export * from "./FoldersCopyResponse.js"; +export * from "./FoldersMoveResponse.js"; +export * from "./FoldersRenameResponse.js"; diff --git a/src/api/resources/index.ts b/src/api/resources/index.ts new file mode 100644 index 00000000..d334b3f6 --- /dev/null +++ b/src/api/resources/index.ts @@ -0,0 +1,14 @@ +export * as files from "./files/index.js"; +export * from "./files/types/index.js"; +export * as assets from "./assets/index.js"; +export * from "./assets/types/index.js"; +export * as folders from "./folders/index.js"; +export * from "./folders/types/index.js"; +export * as accounts from "./accounts/index.js"; +export * as cache from "./cache/index.js"; +export * as customMetadataFields from "./customMetadataFields/index.js"; +export * as beta from "./beta/index.js"; +export * from "./files/client/requests/index.js"; +export * from "./customMetadataFields/client/requests/index.js"; +export * from "./assets/client/requests/index.js"; +export * from "./folders/client/requests/index.js"; diff --git a/src/api/types/AutoDescriptionExtension.ts b/src/api/types/AutoDescriptionExtension.ts new file mode 100644 index 00000000..46e3e1bb --- /dev/null +++ b/src/api/types/AutoDescriptionExtension.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface AutoDescriptionExtension { + /** Specifies the auto description extension. */ + name: "ai-auto-description"; +} diff --git a/src/api/types/AutoTaggingExtension.ts b/src/api/types/AutoTaggingExtension.ts new file mode 100644 index 00000000..98081fb5 --- /dev/null +++ b/src/api/types/AutoTaggingExtension.ts @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface AutoTaggingExtension { + /** Specifies the auto-tagging extension used. */ + name: AutoTaggingExtension.Name; + /** Minimum confidence level for tags to be considered valid. */ + minConfidence: number; + /** Maximum number of tags to attach to the asset. */ + maxTags: number; +} + +export namespace AutoTaggingExtension { + /** + * Specifies the auto-tagging extension used. + */ + export type Name = "google-auto-tagging" | "aws-auto-tagging"; + export const Name = { + GoogleAutoTagging: "google-auto-tagging", + AwsAutoTagging: "aws-auto-tagging", + } as const; +} diff --git a/src/api/types/BadRequestErrorBody.ts b/src/api/types/BadRequestErrorBody.ts new file mode 100644 index 00000000..15bb9ab5 --- /dev/null +++ b/src/api/types/BadRequestErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface BadRequestErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/ConflictErrorBody.ts b/src/api/types/ConflictErrorBody.ts new file mode 100644 index 00000000..d3f3adeb --- /dev/null +++ b/src/api/types/ConflictErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface ConflictErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/CustomMetadataField.ts b/src/api/types/CustomMetadataField.ts new file mode 100644 index 00000000..221127b0 --- /dev/null +++ b/src/api/types/CustomMetadataField.ts @@ -0,0 +1,81 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * 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; + /** 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; + /** 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; + /** 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: Schema.Type; + /** An array of allowed values when field type is `SingleSelect` or `MultiSelect`. */ + selectOptions?: Schema.SelectOptions.Item[]; + /** The default value for this custom metadata field. Date type of default value depends on the field type. */ + defaultValue?: Schema.DefaultValue; + /** Specifies if the this custom metadata field is required or not. */ + isValueRequired?: boolean; + /** 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?: Schema.MinValue; + /** 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?: Schema.MaxValue; + /** Minimum length of string. Only set if `type` is set to `Text` or `Textarea`. */ + minLength?: number; + /** Maximum length of string. Only set if `type` is set to `Text` or `Textarea`. */ + maxLength?: number; + } + + export namespace Schema { + /** + * Type of the custom metadata field. + */ + export type Type = "Text" | "Textarea" | "Number" | "Date" | "Boolean" | "SingleSelect" | "MultiSelect"; + export const Type = { + Text: "Text", + Textarea: "Textarea", + Number: "Number", + Date: "Date", + Boolean: "Boolean", + SingleSelect: "SingleSelect", + MultiSelect: "MultiSelect", + } as const; + export type SelectOptions = SelectOptions.Item[]; + + export namespace SelectOptions { + export type Item = string | number | boolean; + } + + /** + * The default value for this custom metadata field. Date type of default value depends on the field type. + */ + export type DefaultValue = + | string + | number + | boolean + /** + * Default value should be of type array when custom metadata field type is set to `MultiSelect`. */ + | (string | number | boolean)[]; + /** + * 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. + */ + export type MinValue = string | 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. + */ + export type MaxValue = string | number; + } +} diff --git a/src/api/types/ExifDetails.ts b/src/api/types/ExifDetails.ts new file mode 100644 index 00000000..9d14020d --- /dev/null +++ b/src/api/types/ExifDetails.ts @@ -0,0 +1,35 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Object containing Exif details. + */ +export interface ExifDetails { + 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; + FlashpixVersion?: string; + ColorSpace?: number; + ExifImageWidth?: number; + ExifImageHeight?: number; + InteropOffset?: number; + FocalPlaneXResolution?: number; + FocalPlaneYResolution?: number; + FocalPlaneResolutionUnit?: number; + CustomRendered?: number; + ExposureMode?: number; + WhiteBalance?: number; + SceneCaptureType?: number; +} diff --git a/src/api/types/ExifImage.ts b/src/api/types/ExifImage.ts new file mode 100644 index 00000000..3e47b31a --- /dev/null +++ b/src/api/types/ExifImage.ts @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Object containing EXIF image information. + */ +export interface ExifImage { + Make?: string; + Model?: string; + Orientation?: number; + XResolution?: number; + YResolution?: number; + ResolutionUnit?: number; + Software?: string; + ModifyDate?: string; + YCbCrPositioning?: number; + ExifOffset?: number; + GPSInfo?: number; +} diff --git a/src/api/types/FileDetails.ts b/src/api/types/FileDetails.ts new file mode 100644 index 00000000..d2101738 --- /dev/null +++ b/src/api/types/FileDetails.ts @@ -0,0 +1,76 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Object containing details of a file or file version. + */ +export interface FileDetails { + /** Unique identifier of the asset. */ + fileId?: string; + /** Type of the asset. */ + type?: string; + /** Name of the asset. */ + name?: 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; + /** An array of tags assigned to the file. Tags are used to search files in the media library. */ + tags?: string[]; + /** An array of tags assigned to the file by auto tagging. */ + AITags?: FileDetails.AiTags.Item[]; + /** An object with details of the file version. */ + versionInfo?: FileDetails.VersionInfo; + /** Specifies if the file is private or not. */ + isPrivateFile?: boolean; + /** Specifies if the file is published or not. */ + isPublished?: boolean; + /** An string with custom coordinates of the file. */ + customCoordinates?: string; + /** URL of the file. */ + url?: string; + /** 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 file. Possible values are `image`, `non-image`. */ + fileType?: string; + /** MIME type of the file. */ + mime?: string; + /** Width of the file. */ + width?: number; + /** Height of the file. */ + height?: number; + /** Size of the file in bytes. */ + size?: number; + /** Specifies if the image has an alpha channel. */ + hasAlpha?: boolean; + /** An object with custom metadata for the file. */ + customMetadata?: Record; + /** Date and time when the file was uploaded. The date and time is in ISO8601 format. */ + createdAt?: string; + /** Date and time when the file was last updated. The date and time is in ISO8601 format. */ + updatedAt?: string; +} + +export namespace FileDetails { + export type AiTags = AiTags.Item[]; + + export namespace AiTags { + export interface Item { + /** Name of the tag. */ + name?: string; + /** Confidence score of the tag. */ + confidence?: number; + /** 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; + } +} diff --git a/src/api/types/Folder.ts b/src/api/types/Folder.ts new file mode 100644 index 00000000..9ce7393e --- /dev/null +++ b/src/api/types/Folder.ts @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface Folder { + /** Type of the asset. */ + type?: "folder"; + /** Unique identifier of the asset. */ + folderId?: string; + /** Name of the asset. */ + name?: 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; + /** Date and time when the folder was created. The date and time is in ISO8601 format. */ + createdAt?: string; + /** Date and time when the folder was last updated. The date and time is in ISO8601 format. */ + updatedAt?: string; +} diff --git a/src/api/types/ForbiddenErrorBody.ts b/src/api/types/ForbiddenErrorBody.ts new file mode 100644 index 00000000..739bccdf --- /dev/null +++ b/src/api/types/ForbiddenErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface ForbiddenErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/Gps.ts b/src/api/types/Gps.ts new file mode 100644 index 00000000..9c05f9ba --- /dev/null +++ b/src/api/types/Gps.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Object containing GPS information. + */ +export interface Gps { + GPSVersionID?: number[]; +} diff --git a/src/api/types/Interoperability.ts b/src/api/types/Interoperability.ts new file mode 100644 index 00000000..b1d876a7 --- /dev/null +++ b/src/api/types/Interoperability.ts @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * JSON object. + */ +export interface Interoperability { + InteropIndex?: string; + InteropVersion?: string; +} diff --git a/src/api/types/LockedErrorBody.ts b/src/api/types/LockedErrorBody.ts new file mode 100644 index 00000000..7921bf25 --- /dev/null +++ b/src/api/types/LockedErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface LockedErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/Metadata.ts b/src/api/types/Metadata.ts new file mode 100644 index 00000000..b5491359 --- /dev/null +++ b/src/api/types/Metadata.ts @@ -0,0 +1,49 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * JSON object containing metadata. + */ +export interface Metadata { + /** The height of the image or video in pixels. */ + height?: number; + /** The width of the image or video in pixels. */ + width?: number; + /** The file size in bytes. */ + size?: number; + /** The format of the file (e.g., 'jpg', 'mp4'). */ + format?: string; + /** Indicates if the image has a color profile. */ + hasColorProfile?: boolean; + /** The quality indicator of the image. */ + quality?: number; + /** The density of the image in DPI. */ + density?: number; + /** Indicates if the image contains transparent areas. */ + hasTransparency?: boolean; + /** Perceptual hash of the image. */ + pHash?: string; + /** The bit rate of the video in kbps (only for video). */ + bitRate?: number; + /** The duration of the video in seconds (only for video). */ + duration?: number; + /** The audio codec used in the video (only for video). */ + audioCodec?: string; + /** The video codec used in the video (only for video). */ + videoCodec?: string; + exif?: Metadata.Exif; +} + +export namespace Metadata { + export interface Exif { + image?: ImageKit.ExifImage; + thumbnail?: ImageKit.Thumbnail; + exif?: ImageKit.ExifDetails; + gps?: ImageKit.Gps; + interoperability?: ImageKit.Interoperability; + makernote?: Record; + } +} diff --git a/src/api/types/NotFoundErrorBody.ts b/src/api/types/NotFoundErrorBody.ts new file mode 100644 index 00000000..52a415ed --- /dev/null +++ b/src/api/types/NotFoundErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface NotFoundErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/OriginResponse.ts b/src/api/types/OriginResponse.ts new file mode 100644 index 00000000..1fa5cef7 --- /dev/null +++ b/src/api/types/OriginResponse.ts @@ -0,0 +1,139 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Origin object as returned by the API (sensitive fields removed). + */ +export type OriginResponse = + | ImageKit.OriginResponse.S3 + | ImageKit.OriginResponse.S3Compatible + | ImageKit.OriginResponse.CloudinaryBackup + | ImageKit.OriginResponse.WebFolder + | ImageKit.OriginResponse.WebProxy + | ImageKit.OriginResponse.Gcs + | ImageKit.OriginResponse.AzureBlob + | ImageKit.OriginResponse.AkeneoPim; + +export namespace OriginResponse { + export interface S3 { + type: "S3"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + prefix?: unknown; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface S3Compatible { + type: "S3_COMPATIBLE"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + prefix?: unknown; + /** Custom S3-compatible endpoint. */ + endpoint: string; + /** Use path-style S3 URLs? */ + s3ForcePathStyle: boolean; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface CloudinaryBackup { + type: "CLOUDINARY_BACKUP"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + prefix?: unknown; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface WebFolder { + type: "WEB_FOLDER"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + baseUrl?: unknown; + /** Forward the Host header to origin? */ + forwardHostHeaderToOrigin: boolean; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface WebProxy { + type: "WEB_PROXY"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface Gcs { + type: "GCS"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + bucket: string; + prefix?: unknown; + clientEmail: string; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface AzureBlob { + type: "AZURE_BLOB"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + container: string; + prefix?: unknown; + accountName: string; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } + + export interface AkeneoPim { + type: "AKENEO_PIM"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + baseUrl?: unknown; + /** Unique identifier for the origin. This is generated by ImageKit when you create a new origin. */ + id?: string; + } +} diff --git a/src/api/types/OriginResponseArray.ts b/src/api/types/OriginResponseArray.ts new file mode 100644 index 00000000..b156d566 --- /dev/null +++ b/src/api/types/OriginResponseArray.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export type OriginResponseArray = ImageKit.OriginResponse[]; diff --git a/src/api/types/OriginSchema.ts b/src/api/types/OriginSchema.ts new file mode 100644 index 00000000..c28c95a3 --- /dev/null +++ b/src/api/types/OriginSchema.ts @@ -0,0 +1,150 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Schema for origin resources. + */ +export type OriginSchema = + | ImageKit.OriginSchema.S3 + | ImageKit.OriginSchema.S3Compatible + | ImageKit.OriginSchema.CloudinaryBackup + | ImageKit.OriginSchema.WebFolder + | ImageKit.OriginSchema.WebProxy + | ImageKit.OriginSchema.Gcs + | ImageKit.OriginSchema.AzureBlob + | ImageKit.OriginSchema.AkeneoPim; + +export namespace OriginSchema { + export interface S3 { + type: "S3"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + /** Path prefix inside the bucket. */ + prefix?: string; + /** Access key for the bucket. */ + accessKey: string; + /** Secret key for the bucket. */ + secretKey: string; + } + + export interface S3Compatible { + type: "S3_COMPATIBLE"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + /** Path prefix inside the bucket. */ + prefix?: string; + /** Access key for the bucket. */ + accessKey: string; + /** Secret key for the bucket. */ + secretKey: string; + /** Custom S3-compatible endpoint. */ + endpoint: string; + /** Use path-style S3 URLs? */ + s3ForcePathStyle?: boolean; + } + + export interface CloudinaryBackup { + type: "CLOUDINARY_BACKUP"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** S3 bucket name. */ + bucket: string; + /** Path prefix inside the bucket. */ + prefix?: string; + /** Access key for the bucket. */ + accessKey: string; + /** Secret key for the bucket. */ + secretKey: string; + } + + export interface WebFolder { + type: "WEB_FOLDER"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** Root URL for the web folder origin. */ + baseUrl: string; + /** Forward the Host header to origin? */ + forwardHostHeaderToOrigin?: boolean; + } + + export interface WebProxy { + type: "WEB_PROXY"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + } + + export interface Gcs { + type: "GCS"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + bucket: string; + prefix?: string; + clientEmail: string; + privateKey: string; + } + + export interface AzureBlob { + type: "AZURE_BLOB"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + container: string; + prefix?: string; + accountName: string; + sasToken: string; + } + + export interface AkeneoPim { + type: "AKENEO_PIM"; + /** Display name of the origin. */ + name: string; + /** Whether to send a Canonical header. */ + includeCanonicalHeader?: boolean; + /** URL used in the Canonical header (if enabled). */ + baseUrlForCanonicalHeader?: string; + /** Akeneo instance base URL. */ + baseUrl: string; + /** Akeneo API client ID. */ + clientId: string; + /** Akeneo API client secret. */ + clientSecret: string; + /** Akeneo API username. */ + username: string; + /** Akeneo API password. */ + password: string; + } +} diff --git a/src/api/types/RemovedotBgExtension.ts b/src/api/types/RemovedotBgExtension.ts new file mode 100644 index 00000000..cb588271 --- /dev/null +++ b/src/api/types/RemovedotBgExtension.ts @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface RemovedotBgExtension { + /** Specifies the background removal extension. */ + name: "remove-bg"; + options?: RemovedotBgExtension.Options; +} + +export namespace RemovedotBgExtension { + 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; + /** Allows semi-transparent regions in the result. Default is true. Note: Semitransparency is currently only supported for car windows. */ + semitransparency?: 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; + } +} diff --git a/src/api/types/Thumbnail.ts b/src/api/types/Thumbnail.ts new file mode 100644 index 00000000..4a2e1f31 --- /dev/null +++ b/src/api/types/Thumbnail.ts @@ -0,0 +1,15 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * Object containing Thumbnail information. + */ +export interface Thumbnail { + Compression?: number; + XResolution?: number; + YResolution?: number; + ResolutionUnit?: number; + ThumbnailOffset?: number; + ThumbnailLength?: number; +} diff --git a/src/api/types/TooManyRequestsErrorBody.ts b/src/api/types/TooManyRequestsErrorBody.ts new file mode 100644 index 00000000..47843e19 --- /dev/null +++ b/src/api/types/TooManyRequestsErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface TooManyRequestsErrorBody { + /** A description of the error, providing details about why the rate limit has been exceeded. */ + message?: string; +} diff --git a/src/api/types/TransformationObject.ts b/src/api/types/TransformationObject.ts new file mode 100644 index 00000000..7f3e5cfe --- /dev/null +++ b/src/api/types/TransformationObject.ts @@ -0,0 +1,27 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * 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 TransformationObject { + /** Transformation string to apply before uploading the file to the Media Library. Useful for optimizing files at ingestion. */ + pre?: string; + /** + * 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?: ImageKit.TransformationObjectPostItem[]; +} diff --git a/src/api/types/TransformationObjectPostItem.ts b/src/api/types/TransformationObjectPostItem.ts new file mode 100644 index 00000000..f79d5c2f --- /dev/null +++ b/src/api/types/TransformationObjectPostItem.ts @@ -0,0 +1,59 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export type TransformationObjectPostItem = + | ImageKit.TransformationObjectPostItem.Transformation + | ImageKit.TransformationObjectPostItem.GifToVideo + | ImageKit.TransformationObjectPostItem.Thumbnail + | ImageKit.TransformationObjectPostItem.Abs; + +export namespace TransformationObjectPostItem { + export interface Transformation { + type: "transformation"; + /** + * Transformation string (e.g. `w-200,h-200`). + * Same syntax as ImageKit URL-based transformations. + */ + value: string; + } + + export interface GifToVideo { + type: "gif-to-video"; + /** + * Optional transformation string to apply to the output video. + * **Example**: `q-80` + */ + value?: string; + } + + export interface Thumbnail { + type: "thumbnail"; + /** + * Optional transformation string. + * **Example**: `w-150,h-150` + */ + value?: string; + } + + export interface Abs { + type: "abs"; + /** List of different representations you want to create separated by an underscore. */ + value: string; + /** Streaming protocol to use (`hls` or `dash`). */ + protocol: TransformationObjectPostItemAbs.Protocol; + } + + export namespace TransformationObjectPostItemAbs { + /** + * Streaming protocol to use (`hls` or `dash`). + */ + export type Protocol = "hls" | "dash"; + export const Protocol = { + Hls: "hls", + Dash: "dash", + } as const; + } +} diff --git a/src/api/types/UnauthorizedErrorBody.ts b/src/api/types/UnauthorizedErrorBody.ts new file mode 100644 index 00000000..b58e9ead --- /dev/null +++ b/src/api/types/UnauthorizedErrorBody.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface UnauthorizedErrorBody { + message?: string; + help?: string; +} diff --git a/src/api/types/Upload.ts b/src/api/types/Upload.ts new file mode 100644 index 00000000..e89cc18c --- /dev/null +++ b/src/api/types/Upload.ts @@ -0,0 +1,126 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Object containing details of a successful upload. + */ +export interface Upload { + /** Unique fileId. Store this fileld in your database, as this will be used to perform update action on this file. */ + fileId?: string; + /** Name of the asset. */ + name?: string; + /** The relative path of the file in the media library e.g. `/marketing-assets/new-banner.jpg`. */ + filePath?: string; + /** A publicly accessible URL of the file. */ + url?: string; + /** In the case of an image, a small thumbnail URL. */ + thumbnailUrl?: string; + /** Height of the image in pixels (Only for images) */ + height?: number; + /** Width of the image in pixels (Only for Images) */ + width?: number; + /** Size of the image file in Bytes. */ + size?: number; + /** The bit rate of the video in kbps (only for video). */ + bitRate?: number; + /** The duration of the video in seconds (only for video). */ + duration?: number; + /** The audio codec used in the video (only for video). */ + audioCodec?: string; + /** The video codec used in the video (only for video). */ + videoCodec?: string; + /** 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?: string[]; + /** An array of tags assigned to the uploaded file by auto tagging. */ + AITags?: Upload.AiTags.Item[]; + /** An object containing the file or file version's `id` (versionId) and `name`. */ + versionInfo?: Upload.VersionInfo; + /** 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; + /** 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; + /** Type of the uploaded file. Possible values are `image`, `non-image`. */ + fileType?: string; + /** 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?: Record; + /** + * 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?: Upload.ExtensionStatus; + /** Legacy metadata. Send `metadata` in `responseFields` in API request to get metadata in the upload API response. */ + metadata?: ImageKit.Metadata; + /** 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?: Record; +} + +export namespace Upload { + export type AiTags = AiTags.Item[]; + + export namespace AiTags { + export interface Item { + /** Name of the tag. */ + name?: string; + /** Confidence score of the tag. */ + confidence?: number; + /** 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; + } + } + + /** + * 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; + } + + /** + * 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 { + "google-auto-tagging"?: ExtensionStatus.GoogleAutoTagging; + "aws-auto-tagging"?: ExtensionStatus.AwsAutoTagging; + "remove-bg"?: ExtensionStatus.RemoveBg; + } + + export namespace ExtensionStatus { + export type GoogleAutoTagging = "success" | "pending" | "failed"; + export const GoogleAutoTagging = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + export type AwsAutoTagging = "success" | "pending" | "failed"; + export const AwsAutoTagging = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + export type RemoveBg = "success" | "pending" | "failed"; + export const RemoveBg = { + Success: "success", + Pending: "pending", + Failed: "failed", + } as const; + } +} diff --git a/src/api/types/UrlEndpointResponse.ts b/src/api/types/UrlEndpointResponse.ts new file mode 100644 index 00000000..a612ccd6 --- /dev/null +++ b/src/api/types/UrlEndpointResponse.ts @@ -0,0 +1,21 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * URL‑endpoint object as returned by the API. + */ +export interface UrlEndpointResponse { + /** Description of the URL endpoint. */ + description: string; + /** Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint). */ + urlPrefix: 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: string[]; + /** Configuration for third-party URL rewriting. */ + urlRewriter?: ImageKit.UrlEndpointResponseUrlRewriter; + /** 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; +} diff --git a/src/api/types/UrlEndpointResponseArray.ts b/src/api/types/UrlEndpointResponseArray.ts new file mode 100644 index 00000000..4cd7abf6 --- /dev/null +++ b/src/api/types/UrlEndpointResponseArray.ts @@ -0,0 +1,7 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export type UrlEndpointResponseArray = ImageKit.UrlEndpointResponse[]; diff --git a/src/api/types/UrlEndpointResponseUrlRewriter.ts b/src/api/types/UrlEndpointResponseUrlRewriter.ts new file mode 100644 index 00000000..d4083418 --- /dev/null +++ b/src/api/types/UrlEndpointResponseUrlRewriter.ts @@ -0,0 +1,29 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Configuration for third-party URL rewriting. + */ +export type UrlEndpointResponseUrlRewriter = + | ImageKit.UrlEndpointResponseUrlRewriter.Cloudinary + | ImageKit.UrlEndpointResponseUrlRewriter.Imgix + | ImageKit.UrlEndpointResponseUrlRewriter.Akamai; + +export namespace UrlEndpointResponseUrlRewriter { + export interface Cloudinary { + type: "CLOUDINARY"; + /** Whether to preserve `/` in the rewritten URL. */ + preserveAssetDeliveryTypes: boolean; + } + + export interface Imgix { + type: "IMGIX"; + } + + export interface Akamai { + type: "AKAMAI"; + } +} diff --git a/src/api/types/UrlEndpointSchema.ts b/src/api/types/UrlEndpointSchema.ts new file mode 100644 index 00000000..91592b1b --- /dev/null +++ b/src/api/types/UrlEndpointSchema.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Schema for URL endpoint resource. + */ +export interface UrlEndpointSchema { + /** Description of the URL endpoint. */ + description: string; + /** Path segment appended to your base URL to form the endpoint (letters, digits, and hyphens only — or empty for the default endpoint). */ + urlPrefix?: 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?: string[]; + /** Configuration for third-party URL rewriting. */ + urlRewriter?: ImageKit.UrlEndpointSchemaUrlRewriter; +} diff --git a/src/api/types/UrlEndpointSchemaUrlRewriter.ts b/src/api/types/UrlEndpointSchemaUrlRewriter.ts new file mode 100644 index 00000000..b29a794c --- /dev/null +++ b/src/api/types/UrlEndpointSchemaUrlRewriter.ts @@ -0,0 +1,29 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +/** + * Configuration for third-party URL rewriting. + */ +export type UrlEndpointSchemaUrlRewriter = + | ImageKit.UrlEndpointSchemaUrlRewriter.Cloudinary + | ImageKit.UrlEndpointSchemaUrlRewriter.Imgix + | ImageKit.UrlEndpointSchemaUrlRewriter.Akamai; + +export namespace UrlEndpointSchemaUrlRewriter { + export interface Cloudinary { + type: "CLOUDINARY"; + /** Whether to preserve `/` in the rewritten URL. */ + preserveAssetDeliveryTypes?: boolean; + } + + export interface Imgix { + type: "IMGIX"; + } + + export interface Akamai { + type: "AKAMAI"; + } +} diff --git a/src/api/types/VideoAsset.ts b/src/api/types/VideoAsset.ts new file mode 100644 index 00000000..c7caa262 --- /dev/null +++ b/src/api/types/VideoAsset.ts @@ -0,0 +1,8 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface VideoAsset { + /** Source asset URL. */ + url: string; +} diff --git a/src/api/types/VideoTransformationAcceptedEvent.ts b/src/api/types/VideoTransformationAcceptedEvent.ts new file mode 100644 index 00000000..0cb42d00 --- /dev/null +++ b/src/api/types/VideoTransformationAcceptedEvent.ts @@ -0,0 +1,21 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export interface VideoTransformationAcceptedEvent { + type: "video.transformation.accepted"; + /** Unique identifier for the event. */ + id: string; + created_at: string; + request: ImageKit.VideoWebhookRequestInfo; + data: VideoTransformationAcceptedEvent.Data; +} + +export namespace VideoTransformationAcceptedEvent { + export interface Data { + asset: ImageKit.VideoAsset; + transformation: ImageKit.VideoTransformationBase; + } +} diff --git a/src/api/types/VideoTransformationBase.ts b/src/api/types/VideoTransformationBase.ts new file mode 100644 index 00000000..8e6dfa82 --- /dev/null +++ b/src/api/types/VideoTransformationBase.ts @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export interface VideoTransformationBase { + type: VideoTransformationBase.Type; + options?: ImageKit.VideoTransformationOptions; +} + +export namespace VideoTransformationBase { + export type Type = "video-transformation" | "gif-to-video" | "video-thumbnail"; + export const Type = { + VideoTransformation: "video-transformation", + GifToVideo: "gif-to-video", + VideoThumbnail: "video-thumbnail", + } as const; +} diff --git a/src/api/types/VideoTransformationErrorEvent.ts b/src/api/types/VideoTransformationErrorEvent.ts new file mode 100644 index 00000000..56f19e5f --- /dev/null +++ b/src/api/types/VideoTransformationErrorEvent.ts @@ -0,0 +1,42 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export interface VideoTransformationErrorEvent { + type: "video.transformation.error"; + /** Unique identifier for the event. */ + id: string; + created_at: string; + request: ImageKit.VideoWebhookRequestInfo; + data: VideoTransformationErrorEvent.Data; +} + +export namespace VideoTransformationErrorEvent { + export interface Data { + asset: ImageKit.VideoAsset; + transformation: Data.Transformation; + } + + export namespace Data { + export interface Transformation extends ImageKit.VideoTransformationBase { + error?: Transformation.Error_; + } + + export namespace Transformation { + export interface Error_ { + reason: Error_.Reason; + } + + export namespace Error_ { + export type Reason = "encoding_failed" | "download_failed" | "internal_server_error"; + export const Reason = { + EncodingFailed: "encoding_failed", + DownloadFailed: "download_failed", + InternalServerError: "internal_server_error", + } as const; + } + } + } +} diff --git a/src/api/types/VideoTransformationOptions.ts b/src/api/types/VideoTransformationOptions.ts new file mode 100644 index 00000000..174c54e8 --- /dev/null +++ b/src/api/types/VideoTransformationOptions.ts @@ -0,0 +1,39 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface VideoTransformationOptions { + video_codec?: VideoTransformationOptions.VideoCodec; + audio_codec?: VideoTransformationOptions.AudioCodec; + auto_rotate?: boolean; + quality?: number; + format?: VideoTransformationOptions.Format; + stream_protocol?: VideoTransformationOptions.StreamProtocol; + variants?: string[]; +} + +export namespace VideoTransformationOptions { + export type VideoCodec = "h264" | "vp9"; + export const VideoCodec = { + H264: "h264", + Vp9: "vp9", + } as const; + export type AudioCodec = "aac" | "opus"; + export const AudioCodec = { + Aac: "aac", + Opus: "opus", + } as const; + export type Format = "mp4" | "webm" | "jpg" | "png" | "webp"; + export const Format = { + Mp4: "mp4", + Webm: "webm", + Jpg: "jpg", + Png: "png", + Webp: "webp", + } as const; + export type StreamProtocol = "HLS" | "DASH"; + export const StreamProtocol = { + Hls: "HLS", + Dash: "DASH", + } as const; +} diff --git a/src/api/types/VideoTransformationReadyEvent.ts b/src/api/types/VideoTransformationReadyEvent.ts new file mode 100644 index 00000000..ddab40ae --- /dev/null +++ b/src/api/types/VideoTransformationReadyEvent.ts @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as ImageKit from "../index.js"; + +export interface VideoTransformationReadyEvent { + type: "video.transformation.ready"; + /** Unique identifier for the event. */ + id: string; + created_at: string; + request: ImageKit.VideoWebhookRequestInfo; + timings?: VideoTransformationReadyEvent.Timings; + data: VideoTransformationReadyEvent.Data; +} + +export namespace VideoTransformationReadyEvent { + export interface Timings { + /** Milliseconds spent downloading the source. */ + download_duration?: number; + /** Milliseconds spent encoding. */ + encoding_duration?: number; + } + + export interface Data { + asset: ImageKit.VideoAsset; + transformation: Data.Transformation; + } + + export namespace Data { + export interface Transformation extends ImageKit.VideoTransformationBase { + output?: Transformation.Output; + } + + export namespace Transformation { + export interface Output { + url: string; + video_metadata?: Output.VideoMetadata; + } + + export namespace Output { + export interface VideoMetadata { + duration: number; + width: number; + height: number; + bitrate: number; + } + } + } + } +} diff --git a/src/api/types/VideoWebhookRequestInfo.ts b/src/api/types/VideoWebhookRequestInfo.ts new file mode 100644 index 00000000..58003dcf --- /dev/null +++ b/src/api/types/VideoWebhookRequestInfo.ts @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export interface VideoWebhookRequestInfo { + /** Unique ID for the originating request. */ + x_request_id: string; + /** URL of the submitted request. */ + url: string; + /** User-Agent header of the originating request. */ + user_agent?: string; +} diff --git a/src/api/types/index.ts b/src/api/types/index.ts new file mode 100644 index 00000000..14cb5337 --- /dev/null +++ b/src/api/types/index.ts @@ -0,0 +1,37 @@ +export * from "./BadRequestErrorBody.js"; +export * from "./UnauthorizedErrorBody.js"; +export * from "./ForbiddenErrorBody.js"; +export * from "./TooManyRequestsErrorBody.js"; +export * from "./NotFoundErrorBody.js"; +export * from "./ConflictErrorBody.js"; +export * from "./LockedErrorBody.js"; +export * from "./OriginSchema.js"; +export * from "./UrlEndpointSchemaUrlRewriter.js"; +export * from "./UrlEndpointSchema.js"; +export * from "./OriginResponseArray.js"; +export * from "./OriginResponse.js"; +export * from "./UrlEndpointResponseArray.js"; +export * from "./UrlEndpointResponseUrlRewriter.js"; +export * from "./UrlEndpointResponse.js"; +export * from "./TransformationObjectPostItem.js"; +export * from "./TransformationObject.js"; +export * from "./RemovedotBgExtension.js"; +export * from "./AutoTaggingExtension.js"; +export * from "./AutoDescriptionExtension.js"; +export * from "./FileDetails.js"; +export * from "./Folder.js"; +export * from "./Upload.js"; +export * from "./ExifImage.js"; +export * from "./Thumbnail.js"; +export * from "./ExifDetails.js"; +export * from "./Gps.js"; +export * from "./Interoperability.js"; +export * from "./Metadata.js"; +export * from "./CustomMetadataField.js"; +export * from "./VideoWebhookRequestInfo.js"; +export * from "./VideoAsset.js"; +export * from "./VideoTransformationOptions.js"; +export * from "./VideoTransformationBase.js"; +export * from "./VideoTransformationAcceptedEvent.js"; +export * from "./VideoTransformationReadyEvent.js"; +export * from "./VideoTransformationErrorEvent.js"; diff --git a/src/core/auth/AuthProvider.ts b/src/core/auth/AuthProvider.ts new file mode 100644 index 00000000..07e0d6ab --- /dev/null +++ b/src/core/auth/AuthProvider.ts @@ -0,0 +1,5 @@ +import { AuthRequest } from "./AuthRequest.js"; + +export interface AuthProvider { + getAuthRequest(): Promise; +} diff --git a/src/core/auth/AuthRequest.ts b/src/core/auth/AuthRequest.ts new file mode 100644 index 00000000..f6218b42 --- /dev/null +++ b/src/core/auth/AuthRequest.ts @@ -0,0 +1,9 @@ +/** + * Request parameters for authentication requests. + */ +export interface AuthRequest { + /** + * The headers to be included in the request. + */ + headers: Record; +} diff --git a/src/core/auth/BasicAuth.ts b/src/core/auth/BasicAuth.ts new file mode 100644 index 00000000..1c0d8835 --- /dev/null +++ b/src/core/auth/BasicAuth.ts @@ -0,0 +1,31 @@ +import { base64Decode, base64Encode } from "../base64.js"; + +export interface BasicAuth { + username: string; + password: string; +} + +const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; + +export const BasicAuth = { + toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { + if (basicAuth == null) { + return undefined; + } + const token = base64Encode(`${basicAuth.username}:${basicAuth.password}`); + return `Basic ${token}`; + }, + fromAuthorizationHeader: (header: string): BasicAuth => { + const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); + const decoded = base64Decode(credentials); + const [username, password] = decoded.split(":", 2); + + if (username == null || password == null) { + throw new Error("Invalid basic auth"); + } + return { + username, + password, + }; + }, +}; diff --git a/src/core/auth/BearerToken.ts b/src/core/auth/BearerToken.ts new file mode 100644 index 00000000..fe987fc9 --- /dev/null +++ b/src/core/auth/BearerToken.ts @@ -0,0 +1,15 @@ +export type BearerToken = string; + +const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; + +export const BearerToken = { + toAuthorizationHeader: (token: BearerToken | undefined): string | undefined => { + if (token == null) { + return undefined; + } + return `Bearer ${token}`; + }, + fromAuthorizationHeader: (header: string): BearerToken => { + return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; + }, +}; diff --git a/src/core/auth/index.ts b/src/core/auth/index.ts new file mode 100644 index 00000000..7faab9d3 --- /dev/null +++ b/src/core/auth/index.ts @@ -0,0 +1,4 @@ +export { AuthProvider } from "./AuthProvider.js"; +export { type AuthRequest } from "./AuthRequest.js"; +export { BasicAuth } from "./BasicAuth.js"; +export { BearerToken } from "./BearerToken.js"; diff --git a/src/core/base64.ts b/src/core/base64.ts new file mode 100644 index 00000000..448a0db6 --- /dev/null +++ b/src/core/base64.ts @@ -0,0 +1,27 @@ +function base64ToBytes(base64: string): Uint8Array { + const binString = atob(base64); + return Uint8Array.from(binString, (m) => m.codePointAt(0)!); +} + +function bytesToBase64(bytes: Uint8Array): string { + const binString = String.fromCodePoint(...bytes); + return btoa(binString); +} + +export function base64Encode(input: string): string { + if (typeof Buffer !== "undefined") { + return Buffer.from(input, "utf8").toString("base64"); + } + + const bytes = new TextEncoder().encode(input); + return bytesToBase64(bytes); +} + +export function base64Decode(input: string): string { + if (typeof Buffer !== "undefined") { + return Buffer.from(input, "base64").toString("utf8"); + } + + const bytes = base64ToBytes(input); + return new TextDecoder().decode(bytes); +} diff --git a/src/core/exports.ts b/src/core/exports.ts new file mode 100644 index 00000000..e415a8f6 --- /dev/null +++ b/src/core/exports.ts @@ -0,0 +1 @@ +export * from "./file/exports.js"; diff --git a/src/core/fetcher/APIResponse.ts b/src/core/fetcher/APIResponse.ts new file mode 100644 index 00000000..dd4b9466 --- /dev/null +++ b/src/core/fetcher/APIResponse.ts @@ -0,0 +1,23 @@ +import { RawResponse } from "./RawResponse.js"; + +/** + * The response of an API call. + * It is a successful response or a failed response. + */ +export type APIResponse = SuccessfulResponse | FailedResponse; + +export interface SuccessfulResponse { + ok: true; + body: T; + /** + * @deprecated Use `rawResponse` instead + */ + headers?: Record; + rawResponse: RawResponse; +} + +export interface FailedResponse { + ok: false; + error: T; + rawResponse: RawResponse; +} diff --git a/src/core/fetcher/BinaryResponse.ts b/src/core/fetcher/BinaryResponse.ts new file mode 100644 index 00000000..614cb59b --- /dev/null +++ b/src/core/fetcher/BinaryResponse.ts @@ -0,0 +1,36 @@ +import { ResponseWithBody } from "./ResponseWithBody.js"; + +export type BinaryResponse = { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + bodyUsed: boolean; + /** + * Returns a ReadableStream of the response body. + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) + */ + stream: () => ReadableStream; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer: () => Promise; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob: () => Promise; + /** + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) + * Some versions of the Fetch API may not support this method. + */ + bytes?(): Promise; +}; + +export function getBinaryResponse(response: ResponseWithBody): BinaryResponse { + const binaryResponse: BinaryResponse = { + get bodyUsed() { + return response.bodyUsed; + }, + stream: () => response.body, + arrayBuffer: response.arrayBuffer.bind(response), + blob: response.blob.bind(response), + }; + if ("bytes" in response && typeof response.bytes === "function") { + binaryResponse.bytes = response.bytes.bind(response); + } + + return binaryResponse; +} diff --git a/src/core/fetcher/Fetcher.ts b/src/core/fetcher/Fetcher.ts new file mode 100644 index 00000000..9e58ba78 --- /dev/null +++ b/src/core/fetcher/Fetcher.ts @@ -0,0 +1,163 @@ +import { toJson } from "../json.js"; +import { APIResponse } from "./APIResponse.js"; +import { createRequestUrl } from "./createRequestUrl.js"; +import { getErrorResponseBody } from "./getErrorResponseBody.js"; +import { getFetchFn } from "./getFetchFn.js"; +import { getRequestBody } from "./getRequestBody.js"; +import { getResponseBody } from "./getResponseBody.js"; +import { makeRequest } from "./makeRequest.js"; +import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; +import { requestWithRetries } from "./requestWithRetries.js"; +import { Supplier } from "./Supplier.js"; + +export type FetchFunction = (args: Fetcher.Args) => Promise>; + +export declare namespace Fetcher { + export interface Args { + url: string; + method: string; + contentType?: string; + headers?: Record | undefined>; + queryParameters?: Record; + body?: unknown; + timeoutMs?: number; + maxRetries?: number; + withCredentials?: boolean; + abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes"; + responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer" | "binary-response"; + duplex?: "half"; + } + + export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; + + export interface FailedStatusCodeError { + reason: "status-code"; + statusCode: number; + body: unknown; + } + + export interface NonJsonError { + reason: "non-json"; + statusCode: number; + rawBody: string; + } + + export interface TimeoutError { + reason: "timeout"; + } + + export interface UnknownError { + reason: "unknown"; + errorMessage: string; + } +} + +async function getHeaders(args: Fetcher.Args): Promise> { + const newHeaders: Record = {}; + if (args.body !== undefined && args.contentType != null) { + newHeaders["Content-Type"] = args.contentType; + } + + if (args.headers == null) { + return newHeaders; + } + + for (const [key, value] of Object.entries(args.headers)) { + const result = await Supplier.get(value); + if (typeof result === "string") { + newHeaders[key] = result; + continue; + } + if (result == null) { + continue; + } + newHeaders[key] = `${result}`; + } + return newHeaders; +} + +export async function fetcherImpl(args: Fetcher.Args): Promise> { + const url = createRequestUrl(args.url, args.queryParameters); + const requestBody: BodyInit | undefined = await getRequestBody({ + body: args.body, + type: args.requestType === "json" ? "json" : "other", + }); + const fetchFn = await getFetchFn(); + + try { + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + url, + args.method, + await getHeaders(args), + requestBody, + args.timeoutMs, + args.abortSignal, + args.withCredentials, + args.duplex, + ), + args.maxRetries, + ); + + if (response.status >= 200 && response.status < 400) { + return { + ok: true, + body: (await getResponseBody(response, args.responseType)) as R, + headers: response.headers, + rawResponse: toRawResponse(response), + }; + } else { + return { + ok: false, + error: { + reason: "status-code", + statusCode: response.status, + body: await getErrorResponseBody(response), + }, + rawResponse: toRawResponse(response), + }; + } + } catch (error) { + if (args.abortSignal != null && args.abortSignal.aborted) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: "The user aborted a request", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error && error.name === "AbortError") { + return { + ok: false, + error: { + reason: "timeout", + }, + rawResponse: abortRawResponse, + }; + } else if (error instanceof Error) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: error.message, + }, + rawResponse: unknownRawResponse, + }; + } + + return { + ok: false, + error: { + reason: "unknown", + errorMessage: toJson(error), + }, + rawResponse: unknownRawResponse, + }; + } +} + +export const fetcher: FetchFunction = fetcherImpl; diff --git a/src/core/fetcher/Headers.ts b/src/core/fetcher/Headers.ts new file mode 100644 index 00000000..af841aa2 --- /dev/null +++ b/src/core/fetcher/Headers.ts @@ -0,0 +1,93 @@ +let Headers: typeof globalThis.Headers; + +if (typeof globalThis.Headers !== "undefined") { + Headers = globalThis.Headers; +} else { + Headers = class Headers implements Headers { + private headers: Map; + + constructor(init?: HeadersInit) { + this.headers = new Map(); + + if (init) { + if (init instanceof Headers) { + init.forEach((value, key) => this.append(key, value)); + } else if (Array.isArray(init)) { + for (const [key, value] of init) { + if (typeof key === "string" && typeof value === "string") { + this.append(key, value); + } else { + throw new TypeError("Each header entry must be a [string, string] tuple"); + } + } + } else { + for (const [key, value] of Object.entries(init)) { + if (typeof value === "string") { + this.append(key, value); + } else { + throw new TypeError("Header values must be strings"); + } + } + } + } + } + + append(name: string, value: string): void { + const key = name.toLowerCase(); + const existing = this.headers.get(key) || []; + this.headers.set(key, [...existing, value]); + } + + delete(name: string): void { + const key = name.toLowerCase(); + this.headers.delete(key); + } + + get(name: string): string | null { + const key = name.toLowerCase(); + const values = this.headers.get(key); + return values ? values.join(", ") : null; + } + + has(name: string): boolean { + const key = name.toLowerCase(); + return this.headers.has(key); + } + + set(name: string, value: string): void { + const key = name.toLowerCase(); + this.headers.set(key, [value]); + } + + forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: unknown): void { + const boundCallback = thisArg ? callbackfn.bind(thisArg) : callbackfn; + this.headers.forEach((values, key) => boundCallback(values.join(", "), key, this)); + } + + getSetCookie(): string[] { + return this.headers.get("set-cookie") || []; + } + + *entries(): HeadersIterator<[string, string]> { + for (const [key, values] of this.headers.entries()) { + yield [key, values.join(", ")]; + } + } + + *keys(): HeadersIterator { + yield* this.headers.keys(); + } + + *values(): HeadersIterator { + for (const values of this.headers.values()) { + yield values.join(", "); + } + } + + [Symbol.iterator](): HeadersIterator<[string, string]> { + return this.entries(); + } + }; +} + +export { Headers }; diff --git a/src/core/fetcher/HttpResponsePromise.ts b/src/core/fetcher/HttpResponsePromise.ts new file mode 100644 index 00000000..026d88f1 --- /dev/null +++ b/src/core/fetcher/HttpResponsePromise.ts @@ -0,0 +1,116 @@ +import { WithRawResponse } from "./RawResponse.js"; + +/** + * A promise that returns the parsed response and lets you retrieve the raw response too. + */ +export class HttpResponsePromise extends Promise { + private innerPromise: Promise>; + private unwrappedPromise: Promise | undefined; + + private constructor(promise: Promise>) { + // Initialize with a no-op to avoid premature parsing + super((resolve) => { + resolve(undefined as unknown as T); + }); + this.innerPromise = promise; + } + + /** + * Creates an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @param args - Arguments to pass to the function. + * @returns An `HttpResponsePromise` instance. + */ + public static fromFunction Promise>, T>( + fn: F, + ...args: Parameters + ): HttpResponsePromise { + return new HttpResponsePromise(fn(...args)); + } + + /** + * Creates a function that returns an `HttpResponsePromise` from a function that returns a promise. + * + * @param fn - A function that returns a promise resolving to a `WithRawResponse` object. + * @returns A function that returns an `HttpResponsePromise` instance. + */ + public static interceptFunction< + F extends (...args: never[]) => Promise>, + T = Awaited>["data"], + >(fn: F): (...args: Parameters) => HttpResponsePromise { + return (...args: Parameters): HttpResponsePromise => { + return HttpResponsePromise.fromPromise(fn(...args)); + }; + } + + /** + * Creates an `HttpResponsePromise` from an existing promise. + * + * @param promise - A promise resolving to a `WithRawResponse` object. + * @returns An `HttpResponsePromise` instance. + */ + public static fromPromise(promise: Promise>): HttpResponsePromise { + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from an executor function. + * + * @param executor - A function that takes resolve and reject callbacks to create a promise. + * @returns An `HttpResponsePromise` instance. + */ + public static fromExecutor( + executor: (resolve: (value: WithRawResponse) => void, reject: (reason?: unknown) => void) => void, + ): HttpResponsePromise { + const promise = new Promise>(executor); + return new HttpResponsePromise(promise); + } + + /** + * Creates an `HttpResponsePromise` from a resolved result. + * + * @param result - A `WithRawResponse` object to resolve immediately. + * @returns An `HttpResponsePromise` instance. + */ + public static fromResult(result: WithRawResponse): HttpResponsePromise { + const promise = Promise.resolve(result); + return new HttpResponsePromise(promise); + } + + private unwrap(): Promise { + if (!this.unwrappedPromise) { + this.unwrappedPromise = this.innerPromise.then(({ data }) => data); + } + return this.unwrappedPromise; + } + + /** @inheritdoc */ + public override then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.unwrap().then(onfulfilled, onrejected); + } + + /** @inheritdoc */ + public override catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.unwrap().catch(onrejected); + } + + /** @inheritdoc */ + public override finally(onfinally?: (() => void) | null): Promise { + return this.unwrap().finally(onfinally); + } + + /** + * Retrieves the data and raw response. + * + * @returns A promise resolving to a `WithRawResponse` object. + */ + public async withRawResponse(): Promise> { + return await this.innerPromise; + } +} diff --git a/src/core/fetcher/RawResponse.ts b/src/core/fetcher/RawResponse.ts new file mode 100644 index 00000000..37fb44e2 --- /dev/null +++ b/src/core/fetcher/RawResponse.ts @@ -0,0 +1,61 @@ +import { Headers } from "./Headers.js"; + +/** + * The raw response from the fetch call excluding the body. + */ +export type RawResponse = Omit< + { + [K in keyof Response as Response[K] extends Function ? never : K]: Response[K]; // strips out functions + }, + "ok" | "body" | "bodyUsed" +>; // strips out body and bodyUsed + +/** + * A raw response indicating that the request was aborted. + */ +export const abortRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 499, + statusText: "Client Closed Request", + type: "error", + url: "", +} as const; + +/** + * A raw response indicating an unknown error. + */ +export const unknownRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 0, + statusText: "Unknown Error", + type: "error", + url: "", +} as const; + +/** + * Converts a `RawResponse` object into a `RawResponse` by extracting its properties, + * excluding the `body` and `bodyUsed` fields. + * + * @param response - The `RawResponse` object to convert. + * @returns A `RawResponse` object containing the extracted properties of the input response. + */ +export function toRawResponse(response: Response): RawResponse { + return { + headers: response.headers, + redirected: response.redirected, + status: response.status, + statusText: response.statusText, + type: response.type, + url: response.url, + }; +} + +/** + * Creates a `RawResponse` from a standard `Response` object. + */ +export interface WithRawResponse { + readonly data: T; + readonly rawResponse: RawResponse; +} diff --git a/src/core/fetcher/ResponseWithBody.ts b/src/core/fetcher/ResponseWithBody.ts new file mode 100644 index 00000000..445d40f8 --- /dev/null +++ b/src/core/fetcher/ResponseWithBody.ts @@ -0,0 +1,7 @@ +export type ResponseWithBody = Response & { + body: ReadableStream; +}; + +export function isResponseWithBody(response: Response): response is ResponseWithBody { + return (response as ResponseWithBody).body != null; +} diff --git a/src/core/fetcher/Supplier.ts b/src/core/fetcher/Supplier.ts new file mode 100644 index 00000000..867c931c --- /dev/null +++ b/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +export type Supplier = T | Promise | (() => T | Promise); + +export const Supplier = { + get: async (supplier: Supplier): Promise => { + if (typeof supplier === "function") { + return (supplier as () => T)(); + } else { + return supplier; + } + }, +}; diff --git a/src/core/fetcher/createRequestUrl.ts b/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 00000000..88e13265 --- /dev/null +++ b/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,6 @@ +import { toQueryString } from "../url/qs.js"; + +export function createRequestUrl(baseUrl: string, queryParameters?: Record): string { + const queryString = toQueryString(queryParameters, { arrayFormat: "repeat" }); + return queryString ? `${baseUrl}?${queryString}` : baseUrl; +} diff --git a/src/core/fetcher/getErrorResponseBody.ts b/src/core/fetcher/getErrorResponseBody.ts new file mode 100644 index 00000000..450424bd --- /dev/null +++ b/src/core/fetcher/getErrorResponseBody.ts @@ -0,0 +1,32 @@ +import { fromJson } from "../json.js"; +import { getResponseBody } from "./getResponseBody.js"; + +export async function getErrorResponseBody(response: Response): Promise { + let contentType = response.headers.get("Content-Type")?.toLowerCase(); + if (contentType == null || contentType.length === 0) { + return getResponseBody(response); + } + + if (contentType.indexOf(";") !== -1) { + contentType = contentType.split(";")[0]?.trim() ?? ""; + } + switch (contentType) { + case "application/hal+json": + case "application/json": + case "application/ld+json": + case "application/problem+json": + case "application/vnd.api+json": + case "text/json": + const text = await response.text(); + return text.length > 0 ? fromJson(text) : undefined; + default: + if (contentType.startsWith("application/vnd.") && contentType.endsWith("+json")) { + const text = await response.text(); + return text.length > 0 ? fromJson(text) : undefined; + } + + // Fallback to plain text if content type is not recognized + // Even if no body is present, the response will be an empty string + return await response.text(); + } +} diff --git a/src/core/fetcher/getFetchFn.ts b/src/core/fetcher/getFetchFn.ts new file mode 100644 index 00000000..9f845b95 --- /dev/null +++ b/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,3 @@ +export async function getFetchFn(): Promise { + return fetch; +} diff --git a/src/core/fetcher/getHeader.ts b/src/core/fetcher/getHeader.ts new file mode 100644 index 00000000..50f922b0 --- /dev/null +++ b/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +export function getHeader(headers: Record, header: string): string | undefined { + for (const [headerKey, headerValue] of Object.entries(headers)) { + if (headerKey.toLowerCase() === header.toLowerCase()) { + return headerValue; + } + } + return undefined; +} diff --git a/src/core/fetcher/getRequestBody.ts b/src/core/fetcher/getRequestBody.ts new file mode 100644 index 00000000..e38457c5 --- /dev/null +++ b/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,16 @@ +import { toJson } from "../json.js"; + +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "other"; + } +} + +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type.includes("json")) { + return toJson(body); + } else { + return body as BodyInit; + } +} diff --git a/src/core/fetcher/getResponseBody.ts b/src/core/fetcher/getResponseBody.ts new file mode 100644 index 00000000..7ca8b3d2 --- /dev/null +++ b/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,43 @@ +import { getBinaryResponse } from "./BinaryResponse.js"; +import { isResponseWithBody } from "./ResponseWithBody.js"; +import { fromJson } from "../json.js"; + +export async function getResponseBody(response: Response, responseType?: string): Promise { + if (!isResponseWithBody(response)) { + return undefined; + } + switch (responseType) { + case "binary-response": + return getBinaryResponse(response); + case "blob": + return await response.blob(); + case "arrayBuffer": + return await response.arrayBuffer(); + case "sse": + return response.body; + case "streaming": + return response.body; + + case "text": + return await response.text(); + } + + // if responseType is "json" or not specified, try to parse as JSON + const text = await response.text(); + if (text.length > 0) { + try { + let responseBody = fromJson(text); + return responseBody; + } catch (err) { + return { + ok: false, + error: { + reason: "non-json", + statusCode: response.status, + rawBody: text, + }, + }; + } + } + return undefined; +} diff --git a/src/core/fetcher/index.ts b/src/core/fetcher/index.ts new file mode 100644 index 00000000..a131e346 --- /dev/null +++ b/src/core/fetcher/index.ts @@ -0,0 +1,9 @@ +export type { APIResponse } from "./APIResponse.js"; +export type { BinaryResponse } from "./BinaryResponse.js"; +export type { Fetcher, FetchFunction } from "./Fetcher.js"; +export { fetcher } from "./Fetcher.js"; +export { getHeader } from "./getHeader.js"; +export { HttpResponsePromise } from "./HttpResponsePromise.js"; +export type { RawResponse, WithRawResponse } from "./RawResponse.js"; +export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; +export { Supplier } from "./Supplier.js"; diff --git a/src/core/fetcher/makeRequest.ts b/src/core/fetcher/makeRequest.ts new file mode 100644 index 00000000..1a5ffd3c --- /dev/null +++ b/src/core/fetcher/makeRequest.ts @@ -0,0 +1,44 @@ +import { anySignal, getTimeoutSignal } from "./signals.js"; + +export const makeRequest = async ( + fetchFn: (url: string, init: RequestInit) => Promise, + url: string, + method: string, + headers: Record, + requestBody: BodyInit | undefined, + timeoutMs?: number, + abortSignal?: AbortSignal, + withCredentials?: boolean, + duplex?: "half", +): Promise => { + const signals: AbortSignal[] = []; + + // Add timeout signal + let timeoutAbortId: NodeJS.Timeout | undefined = undefined; + if (timeoutMs != null) { + const { signal, abortId } = getTimeoutSignal(timeoutMs); + timeoutAbortId = abortId; + signals.push(signal); + } + + // Add arbitrary signal + if (abortSignal != null) { + signals.push(abortSignal); + } + let newSignals = anySignal(signals); + const response = await fetchFn(url, { + method: method, + headers, + body: requestBody, + signal: newSignals, + credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, + }); + + if (timeoutAbortId != null) { + clearTimeout(timeoutAbortId); + } + + return response; +}; diff --git a/src/core/fetcher/requestWithRetries.ts b/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 00000000..add3cce0 --- /dev/null +++ b/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,33 @@ +const INITIAL_RETRY_DELAY = 1000; // in milliseconds +const MAX_RETRY_DELAY = 60000; // in milliseconds +const DEFAULT_MAX_RETRIES = 2; +const JITTER_FACTOR = 0.2; // 20% random jitter + +function addJitter(delay: number): number { + // Generate a random value between -JITTER_FACTOR and +JITTER_FACTOR + const jitterMultiplier = 1 + (Math.random() * 2 - 1) * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +export async function requestWithRetries( + requestFn: () => Promise, + maxRetries: number = DEFAULT_MAX_RETRIES, +): Promise { + let response: Response = await requestFn(); + + for (let i = 0; i < maxRetries; ++i) { + if ([408, 429].includes(response.status) || response.status >= 500) { + // Calculate base delay using exponential backoff (in milliseconds) + const baseDelay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, i), MAX_RETRY_DELAY); + + // Add jitter to the delay + const delayWithJitter = addJitter(baseDelay); + + await new Promise((resolve) => setTimeout(resolve, delayWithJitter)); + response = await requestFn(); + } else { + break; + } + } + return response!; +} diff --git a/src/core/fetcher/signals.ts b/src/core/fetcher/signals.ts new file mode 100644 index 00000000..a8d32a2e --- /dev/null +++ b/src/core/fetcher/signals.ts @@ -0,0 +1,38 @@ +const TIMEOUT = "timeout"; + +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: NodeJS.Timeout } { + const controller = new AbortController(); + const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); + return { signal: controller.signal, abortId }; +} + +/** + * Returns an abort signal that is getting aborted when + * at least one of the specified abort signals is aborted. + * + * Requires at least node.js 18. + */ +export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { + // Allowing signals to be passed either as array + // of signals or as multiple arguments. + const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; + + const controller = new AbortController(); + + for (const signal of signals) { + if (signal.aborted) { + // Exiting early if one of the signals + // is already aborted. + controller.abort((signal as any)?.reason); + break; + } + + // Listening for signals and removing the listeners + // when at least one symbol is aborted. + signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { + signal: controller.signal, + }); + } + + return controller.signal; +} diff --git a/src/core/file/exports.ts b/src/core/file/exports.ts new file mode 100644 index 00000000..acda0367 --- /dev/null +++ b/src/core/file/exports.ts @@ -0,0 +1 @@ +export { Uploadable } from "./types.js"; diff --git a/src/core/file/file.ts b/src/core/file/file.ts new file mode 100644 index 00000000..bc4b7e50 --- /dev/null +++ b/src/core/file/file.ts @@ -0,0 +1,186 @@ +import { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +async function getFileWithMetadata(file: Uploadable): Promise { + if (isFileLike(file)) { + return getFileWithMetadata({ + data: file, + }); + } + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = file.contentLength ?? (await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = file.contentLength ?? (await tryGetContentLengthFromFileLike(data)); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike(data: Uploadable.FileLike): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/src/core/file/index.ts b/src/core/file/index.ts new file mode 100644 index 00000000..fc16dd52 --- /dev/null +++ b/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/src/core/file/types.ts b/src/core/file/types.ts new file mode 100644 index 00000000..531b6927 --- /dev/null +++ b/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/src/core/form-data-utils/FormDataWrapper.ts b/src/core/form-data-utils/FormDataWrapper.ts new file mode 100644 index 00000000..fa1ca9b9 --- /dev/null +++ b/src/core/form-data-utils/FormDataWrapper.ts @@ -0,0 +1,176 @@ +import { toJson } from "../../core/json.js"; +import { RUNTIME } from "../runtime/index.js"; + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return ArrayBuffer.isView(value); +} + +interface FormDataRequest { + body: Body; + headers: Record; + duplex?: "half"; +} + +function getLastPathSegment(pathStr: string): string { + const lastForwardSlash = pathStr.lastIndexOf("/"); + const lastBackSlash = pathStr.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? pathStr.substring(lastSlashIndex + 1) : pathStr; +} + +async function streamToBuffer(stream: unknown): Promise { + if (RUNTIME.type === "node") { + const { Readable } = await import("stream"); + + if (stream instanceof Readable) { + const chunks: Buffer[] = []; + for await (const chunk of stream) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + return Buffer.concat(chunks); + } + } + + if (isReadableStream(stream)) { + const reader = stream.getReader(); + const chunks: Uint8Array[] = []; + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + chunks.push(value); + } + } finally { + reader.releaseLock(); + } + + const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + for (const chunk of chunks) { + result.set(chunk, offset); + offset += chunk.length; + } + + return Buffer.from(result); + } + + throw new Error( + "Unsupported stream type: " + typeof stream + ". Expected Node.js Readable stream or Web ReadableStream.", + ); +} + +export async function newFormData(): Promise { + return new FormDataWrapper(); +} + +export class FormDataWrapper { + private fd: FormData = new FormData(); + + public async setup(): Promise { + // noop + } + + public append(key: string, value: unknown): void { + this.fd.append(key, String(value)); + } + + private getFileName(value: unknown, filename?: string): string | undefined { + if (filename != null) { + return filename; + } + if (isNamedValue(value)) { + return value.name; + } + if (isPathedValue(value) && value.path) { + return getLastPathSegment(value.path.toString()); + } + return undefined; + } + + private async convertToBlob(value: unknown): Promise { + if (isStreamLike(value) || isReadableStream(value)) { + const buffer = await streamToBuffer(value); + return new Blob([buffer]); + } + + if (value instanceof Blob) { + return value; + } + + if (isBuffer(value)) { + return new Blob([value]); + } + + if (value instanceof ArrayBuffer) { + return new Blob([value]); + } + + if (isArrayBufferView(value)) { + return new Blob([value]); + } + + if (typeof value === "string") { + return new Blob([value]); + } + + if (typeof value === "object" && value !== null) { + return new Blob([toJson(value)], { type: "application/json" }); + } + + return new Blob([String(value)]); + } + + public async appendFile(key: string, value: unknown, fileName?: string): Promise { + fileName = this.getFileName(value, fileName); + const blob = await this.convertToBlob(value); + + if (fileName) { + this.fd.append(key, blob, fileName); + } else { + this.fd.append(key, blob); + } + } + + public getRequest(): FormDataRequest { + return { + body: this.fd, + headers: {}, + duplex: "half" as const, + }; + } +} diff --git a/src/core/form-data-utils/encodeAsFormParameter.ts b/src/core/form-data-utils/encodeAsFormParameter.ts new file mode 100644 index 00000000..cfc67413 --- /dev/null +++ b/src/core/form-data-utils/encodeAsFormParameter.ts @@ -0,0 +1,12 @@ +import { toQueryString } from "../url/qs.js"; + +export function encodeAsFormParameter(value: unknown): Record { + const stringified = toQueryString(value, { encode: false }); + + const keyValuePairs = stringified.split("&").map((pair) => { + const [key, value] = pair.split("="); + return [key, value] as const; + }); + + return Object.fromEntries(keyValuePairs); +} diff --git a/src/core/form-data-utils/index.ts b/src/core/form-data-utils/index.ts new file mode 100644 index 00000000..1188f80c --- /dev/null +++ b/src/core/form-data-utils/index.ts @@ -0,0 +1,2 @@ +export { encodeAsFormParameter } from "./encodeAsFormParameter.js"; +export * from "./FormDataWrapper.js"; diff --git a/src/core/headers.ts b/src/core/headers.ts new file mode 100644 index 00000000..561314d2 --- /dev/null +++ b/src/core/headers.ts @@ -0,0 +1,35 @@ +import * as core from "./index.js"; + +export function mergeHeaders( + ...headersArray: (Record | undefined> | undefined)[] +): Record> { + const result: Record> = {}; + + for (const [key, value] of headersArray + .filter((headers) => headers != null) + .flatMap((headers) => Object.entries(headers))) { + if (value != null) { + result[key] = value; + } else if (key in result) { + delete result[key]; + } + } + + return result; +} + +export function mergeOnlyDefinedHeaders( + ...headersArray: (Record | undefined> | undefined)[] +): Record> { + const result: Record> = {}; + + for (const [key, value] of headersArray + .filter((headers) => headers != null) + .flatMap((headers) => Object.entries(headers))) { + if (value != null) { + result[key] = value; + } + } + + return result; +} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 00000000..101151e2 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,7 @@ +export * from "./fetcher/index.js"; +export * from "./runtime/index.js"; +export * as url from "./url/index.js"; +export * from "./form-data-utils/index.js"; +export * from "./auth/index.js"; +export * from "./base64.js"; +export * as file from "./file/index.js"; diff --git a/src/core/json.ts b/src/core/json.ts new file mode 100644 index 00000000..c052f324 --- /dev/null +++ b/src/core/json.ts @@ -0,0 +1,27 @@ +/** + * Serialize a value to JSON + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * @returns JSON string + */ +export const toJson = ( + value: unknown, + replacer?: (this: unknown, key: string, value: unknown) => unknown, + space?: string | number, +): string => { + return JSON.stringify(value, replacer, space); +}; + +/** + * Parse JSON string to object, array, or other type + * @param text A valid JSON string. + * @param reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is. + * @returns Parsed object, array, or other type + */ +export function fromJson( + text: string, + reviver?: (this: unknown, key: string, value: unknown) => unknown, +): T { + return JSON.parse(text, reviver); +} diff --git a/src/core/runtime/index.ts b/src/core/runtime/index.ts new file mode 100644 index 00000000..cfab23f9 --- /dev/null +++ b/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime.js"; diff --git a/src/core/runtime/runtime.ts b/src/core/runtime/runtime.ts new file mode 100644 index 00000000..08fd2563 --- /dev/null +++ b/src/core/runtime/runtime.ts @@ -0,0 +1,133 @@ +interface DenoGlobal { + version: { + deno: string; + }; +} + +interface BunGlobal { + version: string; +} + +declare const Deno: DenoGlobal | undefined; +declare const Bun: BunGlobal | undefined; +declare const EdgeRuntime: string | undefined; +declare const self: typeof globalThis.self & { + importScripts?: unknown; +}; + +/** + * A constant that indicates which environment and version the SDK is running in. + */ +export const RUNTIME: Runtime = evaluateRuntime(); + +export interface Runtime { + type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd" | "edge-runtime"; + version?: string; + parsedVersion?: number; +} + +function evaluateRuntime(): Runtime { + /** + * A constant that indicates whether the environment the code is running is a Web Browser. + */ + const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; + if (isBrowser) { + return { + type: "browser", + version: window.navigator.userAgent, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Cloudflare. + * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent + */ + const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; + if (isCloudflare) { + return { + type: "workerd", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Edge Runtime. + * https://vercel.com/docs/functions/runtimes/edge-runtime#check-if-you're-running-on-the-edge-runtime + */ + const isEdgeRuntime = typeof EdgeRuntime === "string"; + if (isEdgeRuntime) { + return { + type: "edge-runtime", + }; + } + + /** + * A constant that indicates whether the environment the code is running is a Web Worker. + */ + const isWebWorker = + typeof self === "object" && + typeof self?.importScripts === "function" && + (self.constructor?.name === "DedicatedWorkerGlobalScope" || + self.constructor?.name === "ServiceWorkerGlobalScope" || + self.constructor?.name === "SharedWorkerGlobalScope"); + if (isWebWorker) { + return { + type: "web-worker", + }; + } + + /** + * A constant that indicates whether the environment the code is running is Deno. + * FYI Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions + */ + const isDeno = + typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; + if (isDeno) { + return { + type: "deno", + version: Deno.version.deno, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Bun.sh. + */ + const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; + if (isBun) { + return { + type: "bun", + version: Bun.version, + }; + } + + /** + * A constant that indicates whether the environment the code is running is Node.JS. + */ + const isNode = + typeof process !== "undefined" && + "version" in process && + !!process.version && + "versions" in process && + !!process.versions?.node; + if (isNode) { + return { + type: "node", + version: process.versions.node, + parsedVersion: Number(process.versions.node.split(".")[0]), + }; + } + + /** + * A constant that indicates whether the environment the code is running is in React-Native. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ + const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + if (isReactNative) { + return { + type: "react-native", + }; + } + + return { + type: "unknown", + }; +} diff --git a/src/core/url/index.ts b/src/core/url/index.ts new file mode 100644 index 00000000..ed5aa0ff --- /dev/null +++ b/src/core/url/index.ts @@ -0,0 +1,2 @@ +export { join } from "./join.js"; +export { toQueryString } from "./qs.js"; diff --git a/src/core/url/join.ts b/src/core/url/join.ts new file mode 100644 index 00000000..200426be --- /dev/null +++ b/src/core/url/join.ts @@ -0,0 +1,80 @@ +export function join(base: string, ...segments: string[]): string { + if (!base) { + return ""; + } + + if (segments.length === 0) { + return base; + } + + if (base.includes("://")) { + let url: URL; + try { + url = new URL(base); + } catch { + // Fallback to path joining if URL is malformed + return joinPath(base, ...segments); + } + + const lastSegment = segments[segments.length - 1]; + const shouldPreserveTrailingSlash = lastSegment && lastSegment.endsWith("/"); + + for (const segment of segments) { + const cleanSegment = trimSlashes(segment); + if (cleanSegment) { + url.pathname = joinPathSegments(url.pathname, cleanSegment); + } + } + + if (shouldPreserveTrailingSlash && !url.pathname.endsWith("/")) { + url.pathname += "/"; + } + + return url.toString(); + } + + return joinPath(base, ...segments); +} + +function joinPath(base: string, ...segments: string[]): string { + if (segments.length === 0) { + return base; + } + + let result = base; + + const lastSegment = segments[segments.length - 1]; + const shouldPreserveTrailingSlash = lastSegment && lastSegment.endsWith("/"); + + for (const segment of segments) { + const cleanSegment = trimSlashes(segment); + if (cleanSegment) { + result = joinPathSegments(result, cleanSegment); + } + } + + if (shouldPreserveTrailingSlash && !result.endsWith("/")) { + result += "/"; + } + + return result; +} + +function joinPathSegments(left: string, right: string): string { + if (left.endsWith("/")) { + return left + right; + } + return left + "/" + right; +} + +function trimSlashes(str: string): string { + if (!str) return str; + + let start = 0; + let end = str.length; + + if (str.startsWith("/")) start = 1; + if (str.endsWith("/")) end = str.length - 1; + + return start === 0 && end === str.length ? str : str.slice(start, end); +} diff --git a/src/core/url/qs.ts b/src/core/url/qs.ts new file mode 100644 index 00000000..13e89be9 --- /dev/null +++ b/src/core/url/qs.ts @@ -0,0 +1,74 @@ +interface QueryStringOptions { + arrayFormat?: "indices" | "repeat"; + encode?: boolean; +} + +const defaultQsOptions: Required = { + arrayFormat: "indices", + encode: true, +} as const; + +function encodeValue(value: unknown, shouldEncode: boolean): string { + if (value === undefined) { + return ""; + } + if (value === null) { + return ""; + } + const stringValue = String(value); + return shouldEncode ? encodeURIComponent(stringValue) : stringValue; +} + +function stringifyObject(obj: Record, prefix = "", options: Required): string[] { + const parts: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + const fullKey = prefix ? `${prefix}[${key}]` : key; + + if (value === undefined) { + continue; + } + + if (Array.isArray(value)) { + if (value.length === 0) { + continue; + } + for (let i = 0; i < value.length; i++) { + const item = value[i]; + if (item === undefined) { + continue; + } + if (typeof item === "object" && !Array.isArray(item) && item !== null) { + const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; + parts.push(...stringifyObject(item as Record, arrayKey, options)); + } else { + const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey; + const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey; + parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`); + } + } + } else if (typeof value === "object" && value !== null) { + if (Object.keys(value as Record).length === 0) { + continue; + } + parts.push(...stringifyObject(value as Record, fullKey, options)); + } else { + const encodedKey = options.encode ? encodeURIComponent(fullKey) : fullKey; + parts.push(`${encodedKey}=${encodeValue(value, options.encode)}`); + } + } + + return parts; +} + +export function toQueryString(obj: unknown, options?: QueryStringOptions): string { + if (obj == null || typeof obj !== "object") { + return ""; + } + + const parts = stringifyObject(obj as Record, "", { + ...defaultQsOptions, + ...options, + }); + return parts.join("&"); +} diff --git a/src/environments.ts b/src/environments.ts new file mode 100644 index 00000000..4a1e5710 --- /dev/null +++ b/src/environments.ts @@ -0,0 +1,9 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export const ImageKitEnvironment = { + Default: "https://api.imagekit.io", +} as const; + +export type ImageKitEnvironment = typeof ImageKitEnvironment.Default; diff --git a/src/errors/ImageKitError.ts b/src/errors/ImageKitError.ts new file mode 100644 index 00000000..78cfae3b --- /dev/null +++ b/src/errors/ImageKitError.ts @@ -0,0 +1,55 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../core/index.js"; +import { toJson } from "../core/json.js"; + +export class ImageKitError extends Error { + public readonly statusCode?: number; + public readonly body?: unknown; + public readonly rawResponse?: core.RawResponse; + + constructor({ + message, + statusCode, + body, + rawResponse, + }: { + message?: string; + statusCode?: number; + body?: unknown; + rawResponse?: core.RawResponse; + }) { + super(buildMessage({ message, statusCode, body })); + Object.setPrototypeOf(this, ImageKitError.prototype); + this.statusCode = statusCode; + this.body = body; + this.rawResponse = rawResponse; + } +} + +function buildMessage({ + message, + statusCode, + body, +}: { + message: string | undefined; + statusCode: number | undefined; + body: unknown | undefined; +}): string { + let lines: string[] = []; + if (message != null) { + lines.push(message); + } + + if (statusCode != null) { + lines.push(`Status code: ${statusCode.toString()}`); + } + + if (body != null) { + lines.push(`Body: ${toJson(body, undefined, 2)}`); + } + + return lines.join("\n"); +} diff --git a/src/errors/ImageKitTimeoutError.ts b/src/errors/ImageKitTimeoutError.ts new file mode 100644 index 00000000..92a59c53 --- /dev/null +++ b/src/errors/ImageKitTimeoutError.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export class ImageKitTimeoutError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, ImageKitTimeoutError.prototype); + } +} diff --git a/src/errors/index.ts b/src/errors/index.ts new file mode 100644 index 00000000..36cc0e25 --- /dev/null +++ b/src/errors/index.ts @@ -0,0 +1,2 @@ +export { ImageKitError } from "./ImageKitError.js"; +export { ImageKitTimeoutError } from "./ImageKitTimeoutError.js"; diff --git a/src/exports.ts b/src/exports.ts new file mode 100644 index 00000000..7b70ee14 --- /dev/null +++ b/src/exports.ts @@ -0,0 +1 @@ +export * from "./core/exports.js"; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..8cec3772 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +export * as ImageKit from "./api/index.js"; +export { ImageKitError, ImageKitTimeoutError } from "./errors/index.js"; +export { ImageKitClient } from "./Client.js"; +export { ImageKitEnvironment } from "./environments.js"; +export * from "./exports.js"; diff --git a/src/version.ts b/src/version.ts new file mode 100644 index 00000000..d05d49d8 --- /dev/null +++ b/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "6.0.1"; diff --git a/test-e2e.sh b/test-e2e.sh deleted file mode 100644 index e9aa713f..00000000 --- a/test-e2e.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -npm pack -cd tests/e2e -export YARN_CACHE_FOLDER=.cache -cd node-js -rm -rf .cache -yarn init --yes -echo "Installing local bundle from TAR in NodeJS project" -yarn add ../../../imagekit*.tgz -node index.js;test_result=$? -echo $test_result -if [ "$test_result" != "0" ]; then - printf '%s\n' "Final bundle not working in NodeJS project" >&2 - exit 1 -fi -echo "Final bundle working in NodeJS project" - -cd ../typescript -rm -rf .cache -yarn init --yes -yarn add typescript --dev -echo "Installing local bundle from TAR in Typescript project" -yarn add ../../../imagekit*.tgz -npx tsc && node index.js;test_result=$? -echo $test_result -if [ "$test_result" != "0" ]; then - printf '%s\n' "Final bundle not working in Typescript project" >&2 - exit 1 -fi -echo "Final bundle working in Typescript project" - -rm -rf ../../../imagekit*.tgz \ No newline at end of file diff --git a/tests/BrowserTestEnvironment.ts b/tests/BrowserTestEnvironment.ts new file mode 100644 index 00000000..0f32bf7b --- /dev/null +++ b/tests/BrowserTestEnvironment.ts @@ -0,0 +1,17 @@ +import { TestEnvironment } from "jest-environment-jsdom"; + +class BrowserTestEnvironment extends TestEnvironment { + async setup() { + await super.setup(); + this.global.Request = Request; + this.global.Response = Response; + this.global.ReadableStream = ReadableStream; + this.global.TextEncoder = TextEncoder; + this.global.TextDecoder = TextDecoder; + this.global.FormData = FormData; + this.global.File = File; + this.global.Blob = Blob; + } +} + +export default BrowserTestEnvironment; diff --git a/tests/cache.js b/tests/cache.js deleted file mode 100644 index f1f7b2c4..00000000 --- a/tests/cache.js +++ /dev/null @@ -1,186 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Cache purge API", function () { - describe("Request body check", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - url: url - }); - done(); - return [200]; - }) - - imagekit.purgeCache(url); - }); - - it('Purge cache no url', function (done) { - imagekit.purgeCache("", function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing URL parameter for this request" - }); - done(); - }); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - done(); - return [200]; - }) - - imagekit.getPurgeCacheStatus(requestId); - }); - - it('Purge cache missing requestId', function (done) { - imagekit.getPurgeCacheStatus("", function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing Request ID parameter for this request" - }); - done(); - }); - }); - }); - - describe("Success callbacks", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.purgeCache(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getPurgeCacheStatus(requestId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Purge cache promise', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - var response = await imagekit.getPurgeCacheStatus(requestId); - expect(response).to.be.deep.equal(dummyAPISuccessResponse); - return Promise.resolve(); - }); - }); - - describe("Error callbacks", function () { - it('Purge cache', function (done) { - var url = "http://ik.imagekit.io/demo/default-image.jpg"; - - const scope = nock('https://api.imagekit.io') - .post("/v1/files/purge") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.purgeCache(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Purge cache', function (done) { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getPurgeCacheStatus(requestId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Purge cache promise', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch(ex) { - expect(ex).to.be.deep.equal(dummyAPIErrorResponse); - return Promise.resolve(); - } - - return Promise.reject(); - }); - }); -}); - diff --git a/tests/custom-metadata-field.js b/tests/custom-metadata-field.js deleted file mode 100644 index 18ad8cac..00000000 --- a/tests/custom-metadata-field.js +++ /dev/null @@ -1,392 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - id: "id", - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Custom metadata field API", function () { - describe("Request body check", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }); - done(); - return [200]; - }) - - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }); - }); - - it('Create field missing name', function (done) { - imagekit.createCustomMetadataField({ - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing name parameter for this request" - }); - done(); - }); - }); - - it('Create field missing label', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - schema: { - type: "number", - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing label parameter for this request" - }); - done(); - }); - }); - - it('Create field missing schema', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - label: "label" - }, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing schema parameter for this request" - }); - done(); - }); - }); - - it('Create field missing schema.type', function (done) { - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - minValue: 10 - } - }, (err, response) => { - expect(err).to.deep.equal({ - help: "schema should have a mandatory type field.", - message: "Invalid value for schema" - }); - done(); - }); - }); - - it('Get field', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [200]; - }) - - imagekit.getCustomMetadataFields(); - }); - - it('Get field - includeDeleted true', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: true - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [200]; - }) - - imagekit.getCustomMetadataFields({ - includeDeleted: true - }); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - schema: { - minValue: 10 - } - }); - done(); - return [200]; - }) - - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 10 - } - }); - }); - - it('Update field only label', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - label: "new-label" - }); - done(); - return [200]; - }) - - imagekit.updateCustomMetadataField("fieldId", { - label: "new-label" - }); - }); - - it('Update field missing fieldId', function (done) { - imagekit.updateCustomMetadataField(null, {}, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing fieldId parameter for this request" - }); - done(); - }); - }); - - it('Update field missing label and schema', function (done) { - imagekit.updateCustomMetadataField("fieldId", {}, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Both label and schema is missing" - }); - done(); - }); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(requestBody).to.be.empty; - done(); - return [204]; - }) - - imagekit.deleteCustomMetadataField("fieldId"); - }); - - it('Delete field missing fieldId', function (done) { - imagekit.deleteCustomMetadataField(null, (err, response) => { - expect(err).to.deep.equal({ - help: "", - message: "Missing fieldId parameter for this request" - }); - done(); - }); - }); - - }); - - describe("Success callbacks", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get fields', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, [dummyAPISuccessResponse, dummyAPISuccessResponse]) - - var callback = sinon.spy(); - imagekit.getCustomMetadataFields({}, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, [dummyAPISuccessResponse, dummyAPISuccessResponse]); - done(); - }, 50); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 20 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, null) - - var callback = sinon.spy(); - imagekit.deleteCustomMetadataField("fieldId", callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, {}); - done(); - }, 50); - }); - }); - - describe("Error callbacks", function () { - it('Create field', function (done) { - const scope = nock('https://api.imagekit.io') - .post("/v1/customMetadataFields") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.createCustomMetadataField({ - name: "name", - label: "label", - schema: { - type: "number", - minValue: 10 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get fields', function (done) { - const scope = nock('https://api.imagekit.io') - .get("/v1/customMetadataFields") - .query({ - includeDeleted: false - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.getCustomMetadataFields({}, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Update field', function (done) { - const scope = nock('https://api.imagekit.io') - .patch("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.updateCustomMetadataField("fieldId", { - schema: { - minValue: 20 - } - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Delete field', function (done) { - const scope = nock('https://api.imagekit.io') - .delete("/v1/customMetadataFields/fieldId") - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.deleteCustomMetadataField("fieldId", callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - }); -}); - diff --git a/tests/custom.test.ts b/tests/custom.test.ts new file mode 100644 index 00000000..7f5e031c --- /dev/null +++ b/tests/custom.test.ts @@ -0,0 +1,13 @@ +/** + * This is a custom test file, if you wish to add more tests + * to your SDK. + * Be sure to mark this file in `.fernignore`. + * + * If you include example requests/responses in your fern definition, + * you will have tests automatically generated for you. + */ +describe("test", () => { + it("default", () => { + expect(true).toBe(true); + }); +}); diff --git a/tests/data/index.js b/tests/data/index.js deleted file mode 100644 index 6d7d2f51..00000000 --- a/tests/data/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports.initializationParams = { - publicKey: "test_public_key", - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - privateKey: "test_private_key", - authenticationEndpoint: "http://test/auth" -} \ No newline at end of file diff --git a/tests/data/test_image.jpg b/tests/data/test_image.jpg deleted file mode 100644 index 8102e278..00000000 Binary files a/tests/data/test_image.jpg and /dev/null differ diff --git a/tests/e2e/node-js/index.js b/tests/e2e/node-js/index.js deleted file mode 100644 index 0e411d94..00000000 --- a/tests/e2e/node-js/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const ImageKit = require("imagekit"); - -try { - var imagekit = new ImageKit({ - publicKey: "public", - privateKey: "private", - urlEndpoint: "https://ik.imagekit.io/xyz" - }); - - var url = imagekit.url({ - path: "Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png", - transformation: [{ - width: 100, - raw: "sdfdsf" - }], - transformationPosition: "path" - }) - - if (url === "https://ik.imagekit.io/xyz/tr:w-100,sdfdsf/Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png") { - process.exit(0); - } else { - console.log("Invalid URL", url); - process.exit(1); - } - - -} catch (ex) { - console.log(ex) - process.exit(1); -} - diff --git a/tests/e2e/typescript/index.ts b/tests/e2e/typescript/index.ts deleted file mode 100644 index e46bf8b1..00000000 --- a/tests/e2e/typescript/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import ImageKit from "imagekit"; - -try { - var imagekit = new ImageKit({ - publicKey: "public", - privateKey: "private", - urlEndpoint: "https://ik.imagekit.io/xyz" - }); - - var url = imagekit.url({ - path: "Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png", - transformation: [{ - width: 100, - raw: "sdfdsf" - }], - transformationPosition: "path" - }) - - if (url === "https://ik.imagekit.io/xyz/tr:w-100,sdfdsf/Screenshot_2022-07-05_at_1.06.47_PM_2558irHgF.png") { - process.exit(0); - } else { - console.log("Invalid URL", url); - process.exit(1); - } - - -} catch (ex) { - console.log(ex) - process.exit(1); -} \ No newline at end of file diff --git a/tests/e2e/typescript/tsconfig.json b/tests/e2e/typescript/tsconfig.json deleted file mode 100644 index 75dcaeac..00000000 --- a/tests/e2e/typescript/tsconfig.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/tests/helpers/errors.js b/tests/helpers/errors.js deleted file mode 100644 index 464df30c..00000000 --- a/tests/helpers/errors.js +++ /dev/null @@ -1,2 +0,0 @@ -import errors from '../../libs/constants/errorMessages'; -module.exports = { ...errors }; diff --git a/tests/helpers/spies.js b/tests/helpers/spies.js deleted file mode 100644 index 1dbbc24b..00000000 --- a/tests/helpers/spies.js +++ /dev/null @@ -1,11 +0,0 @@ -// packages -import sinon from 'sinon'; -// internal modules -import pHashUtils from "../../utils/phash"; - -// spies -const pHashDistanceSpy = sinon.spy(pHashUtils, 'pHashDistance'); - -module.exports = { - pHashDistanceSpy, -}; diff --git a/tests/initialization.js b/tests/initialization.js deleted file mode 100644 index 883753e1..00000000 --- a/tests/initialization.js +++ /dev/null @@ -1,65 +0,0 @@ -import chai from "chai"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; - -describe("Initialization checks", function () { - var imagekit = new ImageKit(initializationParams); - - it('should throw error', function () { - try { - new ImageKit({}); - } catch(err) { - expect(err.message).to.be.equal('Missing publicKey during ImageKit initialization'); - } - }); - - it('should throw error', function () { - try { - new ImageKit({ - publicKey: "test_public_key" - }); - } catch(err) { - expect(err.message).to.be.equal('Missing privateKey during ImageKit initialization'); - } - }); - - it('should throw error', function () { - try { - new ImageKit({ - publicKey: "test_public_key", - privateKey: "test_private_key" - }); - } catch(err) { - expect(err.message).to.be.equal('Missing urlEndpoint during ImageKit initialization'); - } - }); - - it('callback', function () { - var imagekit = new ImageKit({ - urlEndpoint: "https://ik.imagekit.io/demo", - publicKey: "test_public_key", - privateKey: "test_private_key" - }); - try { - imagekit.getFileDetails("fileId","wrongCallback"); - } catch(err) { - expect(err.message).to.be.equal("Callback must be a function.") - } - }); - - it('should have options object', function () { - expect(imagekit.options).to.be.an('object'); - }); - - it('should have correctly initialized options object.', function () { - expect(imagekit.options).to.have.property('publicKey').to.be.equal(initializationParams.publicKey); - expect(imagekit.options).to.have.property('urlEndpoint').to.be.equal(initializationParams.urlEndpoint); - expect(imagekit.options).to.have.property('authenticationEndpoint').to.be.equal(initializationParams.authenticationEndpoint); - }); - - it("should have callable functions 'url' and 'upload'", function () { - expect(imagekit.url).to.exist.and.to.be.a('function'); - expect(imagekit.upload).to.exist.and.to.be.a('function'); - }); -}); \ No newline at end of file diff --git a/tests/mediaLibrary.js b/tests/mediaLibrary.js deleted file mode 100644 index 6f9defa7..00000000 --- a/tests/mediaLibrary.js +++ /dev/null @@ -1,1705 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -describe("Media library APIs", function () { - describe("Request body check", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}`) - done(); - return [200] - }) - - imagekit.deleteFile(fileId); - }); - - it('Delete single file missing fileId', function (done) { - imagekit.deleteFile(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Delete file versions', function (done) { - var fileId = "23902390239203923"; - var versionId = "versionId" - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}/versions/${versionId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}`) - done(); - return [200] - }) - - imagekit.deleteFileVersion({ - fileId, - versionId - }); - }); - - it('Delete file versions missing fileId', function (done) { - imagekit.deleteFileVersion(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Delete file versions missing versionId', function (done) { - imagekit.deleteFileVersion({ - fileId: "fileId" - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing versionId parameter for this request" - }) - done(); - }); - }); - - it('Bulk add tags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkAddTags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Bulk add tags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkAddTags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove tags', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/removeTags`) - expect(requestBody).to.be.deep.equal({ - fileIds: [fileId, fileId], - tags: ["tag1", "tag2"] - }) - done(); - return [200] - }) - - imagekit.bulkRemoveTags([fileId, fileId], ["tag1", "tag2"]); - }); - - it('Bulk remove tags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkRemoveTags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove tags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkRemoveTags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Bulk remove AITags', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeAITags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/removeAITags`) - expect(requestBody).to.be.deep.equal({ - fileIds: [fileId, fileId], - AITags: ["tag1", "tag2"] - }) - done(); - return [200] - }) - - imagekit.bulkRemoveAITags([fileId, fileId], ["tag1", "tag2"]); - }); - - it('Bulk remove AITags missing fileId', function (done) { - var tags = ['tag1']; - imagekit.bulkRemoveAITags(null, tags, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Bulk remove AITags missing tags', function (done) { - var fileIds = ["23902390239203923"] - imagekit.bulkRemoveAITags(fileIds, null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for tags", - help: "tags should be a non empty array of string like ['tag1', 'tag2']." - }) - done(); - }); - }); - - it('Copy file - default options', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/copy`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/xyz", - destinationPath: "/abc", - includeFileVersions: false - }) - done(); - return [200] - }) - - imagekit.copyFile({ - sourceFilePath: "/xyz", - destinationPath: "/abc" - }); - }); - - it('Copy file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/copy`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/xyz.jpg", - destinationPath: "/abc", - includeFileVersions: true - }) - done(); - return [200] - }) - - imagekit.copyFile({ - sourceFilePath: "/xyz.jpg", - destinationPath: "/abc", - includeFileVersions: true - }); - }); - - it('Copy file invalid folder path', function (done) { - var sourceFilePath = "/file.jpg"; - imagekit.copyFile({ sourceFilePath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy file invalid file path', function (done) { - var destinationPath = "/"; - imagekit.copyFile({ sourceFilePath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFilePath value", - help: "It should be a string like /path/to/file.jpg'" - }) - done(); - }); - }); - - it('Copy file invalid includeFileVersions value', function (done) { - var sourceFilePath = "/sdf.jpg"; - var destinationPath = "/"; - imagekit.copyFile({ sourceFilePath, destinationPath, includeFileVersions: "sdf" }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid includeFileVersions value", - help: "It should be a boolean" - }) - done(); - }); - }); - - it('Move file', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/move`) - expect(requestBody).to.be.deep.equal({ - sourceFilePath: "/abc.jpg", - destinationPath: "/xyz" - }) - done(); - return [200] - }); - - imagekit.moveFile({ sourceFilePath: "/abc.jpg", destinationPath: "/xyz" }); - }); - - it('Move file invalid folder path', function (done) { - var sourceFilePath = "/file.jpg"; - imagekit.moveFile({ sourceFilePath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Move file invalid file path', function (done) { - var destinationPath = "/"; - imagekit.moveFile({ sourceFilePath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFilePath value", - help: "It should be a string like /path/to/file.jpg'" - }) - done(); - }); - }); - - it('Rename file - default purgeCache value', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/rename`) - expect(requestBody).to.be.deep.equal({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: false - }) - done(); - return [200] - }); - - imagekit.renameFile({ - filePath: "/abc.jpg", - newFileName: "test.jpg" - }) - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/rename`) - expect(requestBody).to.be.deep.equal({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: true - }) - done(); - return [200] - }); - - imagekit.renameFile({ - filePath: "/abc.jpg", - newFileName: "test.jpg", - purgeCache: true - }) - }); - - it('Rename file - invalid filePath', function (done) { - imagekit.renameFile({ - filePath: null, - newFileName: "test.jpg" - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for filePath", - help: "Pass the full path of the file. For example - /path/to/file.jpg" - }) - done(); - }); - }); - - it('Rename file - invalid newFileName', function (done) { - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: null, - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for newFileName. It should be a string.", - help: "" - }) - done(); - }); - }); - - it('Rename file - invalid purgeCache', function (done) { - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.pdf", - purgeCache: "sdf" - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for purgeCache. It should be boolean.", - help: "" - }) - done(); - }); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}/restore`) - expect(requestBody).to.be.empty; - done(); - return [200] - }); - - imagekit.restoreFileVersion({ - fileId, - versionId, - }) - }); - - it('Restore file version - missing fileId', function (done) { - imagekit.restoreFileVersion({ - fileId: null, - versionId: "versionId", - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Missing fileId parameter for this request", - help: "" - }) - done(); - }); - }); - - it('Restore file version - missing versionId', function (done) { - imagekit.restoreFileVersion({ - fileId: "fileId", - versionId: null - }, function (err, response) { - expect(err).to.deep.equal({ - message: "Missing versionId parameter for this request", - help: "" - }) - done(); - }); - }); - - it('Copy folder - default options', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/copyFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: false - }) - done(); - return [200] - }); - - imagekit.copyFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - }); - - it('Copy folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/copyFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: true - }) - done(); - return [200] - }); - - imagekit.copyFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination", - includeFileVersions: true - }) - }); - - it('Copy folder invalid sourceFolderPath', function (done) { - var destinationPath = "/"; - imagekit.copyFolder({ sourceFolderPath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy folder invalid destinationPath', function (done) { - var sourceFolderPath = "/"; - imagekit.copyFolder({ sourceFolderPath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Copy folder invalid includeFileVersions', function (done) { - var sourceFolderPath = "/"; - imagekit.copyFolder({ sourceFolderPath, destinationPath: "/sdf", includeFileVersions: "sdf" }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid includeFileVersions value", - help: "It should be a boolean" - }) - done(); - }); - }); - - it('Move folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/moveFolder`) - expect(requestBody).to.be.deep.equal({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - done(); - return [200] - }); - - imagekit.moveFolder({ - sourceFolderPath: "/source-folder", - destinationPath: "/destination" - }) - }); - - it('Move folder invalid destinationPath', function (done) { - var sourceFolderPath = "/"; - imagekit.moveFolder({ sourceFolderPath, destinationPath: null }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid destinationPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Move folder invalid sourceFolderPath', function (done) { - var destinationPath = "/"; - imagekit.moveFolder({ sourceFolderPath: null, destinationPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid sourceFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Create folder', function (done) { - const scope = nock('https://api.imagekit.io') - .post(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/folder`) - expect(requestBody).to.be.deep.equal({ - folderName: "abc", - parentFolderPath: "/path/to/folder" - }) - done(); - return [200] - }); - - imagekit.createFolder({ - folderName: "abc", - parentFolderPath: "/path/to/folder" - }) - }); - - it('Create folder invalid name', function (done) { - var folderName = ""; - var parentFolderPath = ""; - imagekit.createFolder({ folderName, parentFolderPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid folderName value", - help: "" - }) - done(); - }); - }); - - it('Create folder invalid path', function (done) { - var folderName = "folder1"; - var parentFolderPath = ""; - imagekit.createFolder({ folderName, parentFolderPath }, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid parentFolderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Delete folder', function (done) { - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/folder`) - expect(requestBody).to.be.deep.equal({ - folderPath: "/path/to/folder", - }) - done(); - return [200] - }); - - imagekit.deleteFolder("/path/to/folder") - }); - - it('Delete folder invalid path', function (done) { - imagekit.deleteFolder(null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid folderPath value", - help: "It should be a string like '/path/to/folder'" - }) - done(); - }); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/metadata`) - done() - return [200] - }) - - imagekit.getFileMetadata(fileId); - }); - - it('Get file metadata using fileId missing fileId', function (done) { - imagekit.getFileMetadata(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Pass either fileId or remote URL of the image as first parameter" - }) - done(); - }); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`) - done() - return [200] - }) - - imagekit.getFileDetails(fileId); - }); - - it('Get file details missing fileId', function (done) { - imagekit.getFileDetails(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get all file versions', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/versions`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions`) - done() - return [200] - }) - - imagekit.getFileVersions(fileId); - }); - - it('Get all file versions - missing fileId', function (done) { - imagekit.getFileVersions(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get file versions details', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/versions/${versionId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/versions/${versionId}`) - done() - return [200] - }) - - imagekit.getFileVersionDetails({ - fileId, - versionId - }); - }); - - it('Get file versions details - missing fileId', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - imagekit.getFileVersionDetails({ - fileId: null, - versionId - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('Get file versions details - missing versionId', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - - imagekit.getFileVersionDetails({ - fileId, - versionId: null - }, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing versionId parameter for this request" - }) - done(); - }); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100", - extensions: [ - { - name: "google-auto-tagging", - maxTags: 5, - minConfidence: 95 - } - ], - customMetadata: { - SKU: 10 - }, - webhookUrl: "https://some-domain/some-api-id" - } - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`); - expect(requestBody).to.deep.equal(updateData); - done() - }) - - imagekit.updateFileDetails(fileId, updateData); - }); - - - it('Update publish status', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - publish: { - isPublished: false, - }, - }; - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.path).equal(`/v1/files/${fileId}/details`); - expect(requestBody).to.deep.equal(updateData); - done() - }) - - imagekit.updateFileDetails(fileId, updateData); - }); - - it('Update file details invalid updateData', function (done) { - var fileId = "23902390239203923"; - - imagekit.updateFileDetails(fileId, null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing file update data for this request" - }) - done(); - }); - }); - - it('Update file details missing fileId', function (done) { - var updateData = { - tags: "sdf", - customCoordinates: "10,10,100,100" - } - - imagekit.updateFileDetails(null, updateData, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing fileId parameter for this request" - }) - done(); - }); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100, - tags: ["t-shirt", "summer"] - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query({ - skip: listOptions.skip, - limit: listOptions.limit, - tags: listOptions.tags.join(",") - }) - .reply(function (uri, requestBody) { - expect(requestBody).equal("") - done() - return [200] - }) - - imagekit.listFiles(listOptions); - }); - - it('List files empty list options', function (done) { - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(actualQueryParams => { - if (Object.keys(actualQueryParams).length) { - done("query params should have been empty") - } else { - done(); - } - return true; - }) - .reply(function () { - return [200, dummyAPISuccessResponse] - }) - - imagekit.listFiles(); - }); - - it('List files empty invalid options', function (done) { - imagekit.listFiles("invalid", function (err, response) { - expect(err).to.deep.equal({ - message: "Pass a valid JSON list options e.g. {skip: 10, limit: 100}.", - help: "" - }) - done(); - }); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(requestBody).to.deep.equal({ - fileIds: fileIds - }) - done() - }) - - imagekit.bulkDeleteFiles(fileIds); - }); - - it('Bulk file delete by fileids missing fileIds', function (done) { - imagekit.bulkDeleteFiles(null, function (err, response) { - expect(err).to.deep.equal({ - message: "Invalid value for fileIds", - help: "fileIds should be an array of fileId of the files. The array should have atleast one fileId." - }) - done(); - }); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(function (uri, requestBody) { - expect(this.req.path).equal(`/v1/bulkJobs/${jobId}`) - done(); - return [200] - }) - - imagekit.getBulkJobStatus(jobId); - }); - - it('Get bulk job status missing jobId', function (done) { - imagekit.getBulkJobStatus(null, function (err, response) { - expect(err).to.deep.equal({ - help: "", - message: "Missing jobId parameter" - }) - done(); - }); - }); - }); - - describe("Success callbacks", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, null) - - var callback = sinon.spy(); - - imagekit.deleteFile(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, {}); - done(); - }, 50); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get file metadata using remote URL', function (done) { - var url = "https://ik.imagekit.io/demo/image.jpg"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query({ - url: url - }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - imagekit.getFileDetails(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100" - } - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - imagekit.updateFileDetails(fileId, updateData, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100 - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(listOptions) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.listFiles(listOptions, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - - var callback = sinon.spy(); - - imagekit.bulkDeleteFiles(fileIds, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk add tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/addTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.bulkAddTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Bulk remove tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.bulkRemoveTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Copy file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.copyFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Move file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.moveFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.jpg" - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.restoreFileVersion({ - fileId, - versionId - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Copy folder', function (done) { - var sourceFolderPath = "/folder2"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.copyFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Move folder', function (done) { - var sourceFolderPath = "/folder1"; - var destinationPath = "/folder2/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.moveFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.getBulkJobStatus(jobId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Create folder', function (done) { - var folderName = "folder1"; - var parentFolderPath = "/"; - - const scope = nock('https://api.imagekit.io') - .post('/v1/folder') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(201, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.createFolder({ folderName, parentFolderPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - - it('Delete folder', function (done) { - var folderPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(204, dummyAPISuccessResponse) - - var callback = sinon.spy(); - - imagekit.deleteFolder(folderPath, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, dummyAPISuccessResponse); - done(); - }, 50); - }); - }); - - describe("Error callbacks", function () { - it('Delete single file', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/files/${fileId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - imagekit.deleteFile(fileId, callback) - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file metadata using fileId', function (done) { - var fileId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/metadata`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file metadata using remote URL', function (done) { - var url = "https://ik.imagekit.io/demo/image.jpg"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/metadata`) - .query({ - url - }) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getFileMetadata(url, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get file details', function (done) { - var fileId = "23902390239203923"; - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - imagekit.getFileDetails(fileId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Update file details', function (done) { - var fileId = "23902390239203923"; - - var updateData = { - tags: ["tag1", "tag2"], - customCoordinates: "10,10,100,100" - } - - var callback = sinon.spy(); - - const scope = nock('https://api.imagekit.io') - .patch(`/v1/files/${fileId}/details`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - imagekit.updateFileDetails(fileId, updateData, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('List files', function (done) { - var listOptions = { - skip: 0, - limit: 100 - } - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .query(listOptions) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.listFiles(listOptions, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk file delete by fileids', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - - var callback = sinon.spy(); - - imagekit.bulkDeleteFiles(fileIds, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk add tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/addTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.bulkAddTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Bulk remove tags', function (done) { - var fileIds = ["fileId1", "fileId2"]; - var tags = ["tag1", "tag2"]; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/removeTags`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.bulkRemoveTags(fileIds, tags, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Copy file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/copy`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.copyFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Move file', function (done) { - var sourceFilePath = "/file_path.jpg"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/move`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.moveFile({ sourceFilePath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Rename file', function (done) { - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/rename`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.renameFile({ - filePath: "/xyz.jpg", - newFileName: "test.jpg" - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Restore file version', function (done) { - var fileId = "fileId"; - var versionId = "versionId"; - const scope = nock('https://api.imagekit.io') - .put(`/v1/files/${fileId}/versions/${versionId}/restore`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.restoreFileVersion({ - fileId, - versionId - }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Copy folder', function (done) { - var sourceFolderPath = "/folder2"; - var destinationPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/copyFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.copyFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Move folder', function (done) { - var sourceFolderPath = "/folder1"; - var destinationPath = "/folder2/"; - - const scope = nock('https://api.imagekit.io') - .post(`/v1/bulkJobs/moveFolder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.moveFolder({ sourceFolderPath, destinationPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Get bulk job status', function (done) { - var jobId = "23902390239203923"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/bulkJobs/${jobId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.getBulkJobStatus(jobId, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Create folder', function (done) { - var folderName = "folder1"; - var parentFolderPath = "/"; - - const scope = nock('https://api.imagekit.io') - .post('/v1/folder') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.createFolder({ folderName, parentFolderPath }, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Delete folder', function (done) { - var folderPath = "/folder1/"; - - const scope = nock('https://api.imagekit.io') - .delete(`/v1/folder`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(404, dummyAPIErrorResponse) - - var callback = sinon.spy(); - - imagekit.deleteFolder(folderPath, callback); - - setTimeout(function () { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, dummyAPIErrorResponse, null); - done(); - }, 50); - }); - - it('Rate limit error', function (done) { - var fileIds = ["fileId1", "fileId2"]; - - var responseBody = { - message: "rate limit exceeded" - }; - - var rateLimitHeaders = { - "X-RateLimit-Limit": 10, - "X-RateLimit-Reset": 1000, - "X-RateLimit-Interval": 1000 - } - - const scope = nock('https://api.imagekit.io') - .post(`/v1/files/batch/deleteByFileIds`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(() => { - return [ - 429, - responseBody, - rateLimitHeaders - ] - }) - - imagekit.bulkDeleteFiles(fileIds, function (err, response) { - expect(err).deep.equal({ - ...responseBody, - ...rateLimitHeaders - }) - done(); - }); - }); - }); -}); - diff --git a/tests/mock-server/MockServer.ts b/tests/mock-server/MockServer.ts new file mode 100644 index 00000000..6e258f17 --- /dev/null +++ b/tests/mock-server/MockServer.ts @@ -0,0 +1,29 @@ +import { RequestHandlerOptions } from "msw"; +import type { SetupServer } from "msw/node"; + +import { mockEndpointBuilder } from "./mockEndpointBuilder"; + +export interface MockServerOptions { + baseUrl: string; + server: SetupServer; +} + +export class MockServer { + private readonly server: SetupServer; + public readonly baseUrl: string; + + constructor({ baseUrl, server }: MockServerOptions) { + this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl; + this.server = server; + } + + public mockEndpoint(options?: RequestHandlerOptions): ReturnType { + const builder = mockEndpointBuilder({ + once: options?.once, + onBuild: (handler) => { + this.server.use(handler); + }, + }).baseUrl(this.baseUrl); + return builder; + } +} diff --git a/tests/mock-server/MockServerPool.ts b/tests/mock-server/MockServerPool.ts new file mode 100644 index 00000000..81608069 --- /dev/null +++ b/tests/mock-server/MockServerPool.ts @@ -0,0 +1,106 @@ +import { setupServer } from "msw/node"; + +import { fromJson, toJson } from "../../src/core/json"; +import { MockServer } from "./MockServer"; +import { randomBaseUrl } from "./randomBaseUrl"; + +const mswServer = setupServer(); +interface MockServerOptions { + baseUrl?: string; +} + +async function formatHttpRequest(request: Request, id?: string): Promise { + try { + const clone = request.clone(); + const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); + + let body = ""; + try { + const contentType = clone.headers.get("content-type"); + if (contentType?.includes("application/json")) { + body = toJson(fromJson(await clone.text()), undefined, 2); + } else if (clone.body) { + body = await clone.text(); + } + } catch (e) { + body = "(unable to parse body)"; + } + + const title = id ? `### Request ${id} ###\n` : ""; + const firstLine = `${title}${request.method} ${request.url.toString()} HTTP/1.1`; + + return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; + } catch (e) { + return `Error formatting request: ${e}`; + } +} + +async function formatHttpResponse(response: Response, id?: string): Promise { + try { + const clone = response.clone(); + const headers = [...clone.headers.entries()].map(([k, v]) => `${k}: ${v}`).join("\n"); + + let body = ""; + try { + const contentType = clone.headers.get("content-type"); + if (contentType?.includes("application/json")) { + body = toJson(fromJson(await clone.text()), undefined, 2); + } else if (clone.body) { + body = await clone.text(); + } + } catch (e) { + body = "(unable to parse body)"; + } + + const title = id ? `### Response for ${id} ###\n` : ""; + const firstLine = `${title}HTTP/1.1 ${response.status} ${response.statusText}`; + + return `\n${firstLine}\n${headers}\n\n${body || "(no body)"}\n`; + } catch (e) { + return `Error formatting response: ${e}`; + } +} + +class MockServerPool { + private servers: MockServer[] = []; + + public createServer(options?: Partial): MockServer { + const baseUrl = options?.baseUrl || randomBaseUrl(); + const server = new MockServer({ baseUrl, server: mswServer }); + this.servers.push(server); + return server; + } + + public getServers(): MockServer[] { + return [...this.servers]; + } + + public listen(): void { + const onUnhandledRequest = process.env.LOG_LEVEL === "debug" ? "warn" : "bypass"; + mswServer.listen({ onUnhandledRequest }); + + if (process.env.LOG_LEVEL === "debug") { + mswServer.events.on("request:start", async ({ request, requestId }) => { + const formattedRequest = await formatHttpRequest(request, requestId); + console.debug("request:start\n" + formattedRequest); + }); + + mswServer.events.on("request:unhandled", async ({ request, requestId }) => { + const formattedRequest = await formatHttpRequest(request, requestId); + console.debug("request:unhandled\n" + formattedRequest); + }); + + mswServer.events.on("response:mocked", async ({ request, response, requestId }) => { + const formattedResponse = await formatHttpResponse(response, requestId); + console.debug("response:mocked\n" + formattedResponse); + }); + } + } + + public close(): void { + this.servers = []; + mswServer.close(); + } +} + +export const mockServerPool = new MockServerPool(); diff --git a/tests/mock-server/mockEndpointBuilder.ts b/tests/mock-server/mockEndpointBuilder.ts new file mode 100644 index 00000000..0b069b2d --- /dev/null +++ b/tests/mock-server/mockEndpointBuilder.ts @@ -0,0 +1,210 @@ +import { DefaultBodyType, HttpHandler, HttpResponse, HttpResponseResolver, http } from "msw"; + +import { url } from "../../src/core"; +import { toJson } from "../../src/core/json"; +import { withHeaders } from "./withHeaders"; +import { withJson } from "./withJson"; + +type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; + +interface MethodStage { + baseUrl(baseUrl: string): MethodStage; + all(path: string): RequestHeadersStage; + get(path: string): RequestHeadersStage; + post(path: string): RequestHeadersStage; + put(path: string): RequestHeadersStage; + delete(path: string): RequestHeadersStage; + patch(path: string): RequestHeadersStage; + options(path: string): RequestHeadersStage; + head(path: string): RequestHeadersStage; +} + +interface RequestHeadersStage extends RequestBodyStage, ResponseStage { + header(name: string, value: string): RequestHeadersStage; + headers(headers: Record): RequestBodyStage; +} + +interface RequestBodyStage extends ResponseStage { + jsonBody(body: unknown): ResponseStage; +} + +interface ResponseStage { + respondWith(): ResponseStatusStage; +} +interface ResponseStatusStage { + statusCode(statusCode: number): ResponseHeaderStage; +} + +interface ResponseHeaderStage extends ResponseBodyStage, BuildStage { + header(name: string, value: string): ResponseHeaderStage; + headers(headers: Record): ResponseHeaderStage; +} + +interface ResponseBodyStage { + jsonBody(body: unknown): BuildStage; +} + +interface BuildStage { + build(): HttpHandler; +} + +export interface HttpHandlerBuilderOptions { + onBuild?: (handler: HttpHandler) => void; + once?: boolean; +} + +class RequestBuilder implements MethodStage, RequestHeadersStage, RequestBodyStage, ResponseStage { + private method: HttpMethod = "get"; + private _baseUrl: string = ""; + private path: string = "/"; + private readonly predicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[] = []; + private readonly handlerOptions?: HttpHandlerBuilderOptions; + + constructor(options?: HttpHandlerBuilderOptions) { + this.handlerOptions = options; + } + + baseUrl(baseUrl: string): MethodStage { + this._baseUrl = baseUrl; + return this; + } + + all(path: string): RequestHeadersStage { + this.method = "all"; + this.path = path; + return this; + } + + get(path: string): RequestHeadersStage { + this.method = "get"; + this.path = path; + return this; + } + + post(path: string): RequestHeadersStage { + this.method = "post"; + this.path = path; + return this; + } + + put(path: string): RequestHeadersStage { + this.method = "put"; + this.path = path; + return this; + } + + delete(path: string): RequestHeadersStage { + this.method = "delete"; + this.path = path; + return this; + } + + patch(path: string): RequestHeadersStage { + this.method = "patch"; + this.path = path; + return this; + } + + options(path: string): RequestHeadersStage { + this.method = "options"; + this.path = path; + return this; + } + + head(path: string): RequestHeadersStage { + this.method = "head"; + this.path = path; + return this; + } + + header(name: string, value: string): RequestHeadersStage { + this.predicates.push((resolver) => withHeaders({ [name]: value }, resolver)); + return this; + } + + headers(headers: Record): RequestBodyStage { + this.predicates.push((resolver) => withHeaders(headers, resolver)); + return this; + } + + jsonBody(body: unknown): ResponseStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you want an empty body."); + } + this.predicates.push((resolver) => withJson(body, resolver)); + return this; + } + + respondWith(): ResponseStatusStage { + return new ResponseBuilder(this.method, this.buildUrl(), this.predicates, this.handlerOptions); + } + + private buildUrl(): string { + return url.join(this._baseUrl, this.path); + } +} + +class ResponseBuilder implements ResponseStatusStage, ResponseHeaderStage, ResponseBodyStage, BuildStage { + private readonly method: HttpMethod; + private readonly url: string; + private readonly requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[]; + private readonly handlerOptions?: HttpHandlerBuilderOptions; + + private responseStatusCode: number = 200; + private responseHeaders: Record = {}; + private responseBody: DefaultBodyType = undefined; + + constructor( + method: HttpMethod, + url: string, + requestPredicates: ((resolver: HttpResponseResolver) => HttpResponseResolver)[], + options?: HttpHandlerBuilderOptions, + ) { + this.method = method; + this.url = url; + this.requestPredicates = requestPredicates; + this.handlerOptions = options; + } + + public statusCode(code: number): ResponseHeaderStage { + this.responseStatusCode = code; + return this; + } + + public header(name: string, value: string): ResponseHeaderStage { + this.responseHeaders[name] = value; + return this; + } + + public headers(headers: Record): ResponseHeaderStage { + this.responseHeaders = { ...this.responseHeaders, ...headers }; + return this; + } + + public jsonBody(body: unknown): BuildStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you expect an empty body."); + } + this.responseBody = toJson(body); + return this; + } + + public build(): HttpHandler { + const responseResolver: HttpResponseResolver = () => { + return new HttpResponse(this.responseBody, { + status: this.responseStatusCode, + headers: this.responseHeaders, + }); + }; + + const finalResolver = this.requestPredicates.reduceRight((acc, predicate) => predicate(acc), responseResolver); + + const handler = http[this.method](this.url, finalResolver, this.handlerOptions); + this.handlerOptions?.onBuild?.(handler); + return handler; + } +} + +export function mockEndpointBuilder(options?: HttpHandlerBuilderOptions): MethodStage { + return new RequestBuilder(options); +} diff --git a/tests/mock-server/randomBaseUrl.ts b/tests/mock-server/randomBaseUrl.ts new file mode 100644 index 00000000..031aa640 --- /dev/null +++ b/tests/mock-server/randomBaseUrl.ts @@ -0,0 +1,4 @@ +export function randomBaseUrl(): string { + const randomString = Math.random().toString(36).substring(2, 15); + return `http://${randomString}.localhost`; +} diff --git a/tests/mock-server/setup.ts b/tests/mock-server/setup.ts new file mode 100644 index 00000000..c216d607 --- /dev/null +++ b/tests/mock-server/setup.ts @@ -0,0 +1,10 @@ +import { afterAll, beforeAll } from "@jest/globals"; + +import { mockServerPool } from "./MockServerPool"; + +beforeAll(() => { + mockServerPool.listen(); +}); +afterAll(() => { + mockServerPool.close(); +}); diff --git a/tests/mock-server/withHeaders.ts b/tests/mock-server/withHeaders.ts new file mode 100644 index 00000000..e77c837d --- /dev/null +++ b/tests/mock-server/withHeaders.ts @@ -0,0 +1,70 @@ +import { HttpResponseResolver, passthrough } from "msw"; + +/** + * Creates a request matcher that validates if request headers match specified criteria + * @param expectedHeaders - Headers to match against + * @param resolver - Response resolver to execute if headers match + */ +export function withHeaders( + expectedHeaders: Record boolean)>, + resolver: HttpResponseResolver, +): HttpResponseResolver { + return (args) => { + const { request } = args; + const { headers } = request; + + const mismatches: Record< + string, + { actual: string | null; expected: string | RegExp | ((value: string) => boolean) } + > = {}; + + for (const [key, expectedValue] of Object.entries(expectedHeaders)) { + const actualValue = headers.get(key); + + if (actualValue === null) { + mismatches[key] = { actual: null, expected: expectedValue }; + continue; + } + + if (typeof expectedValue === "function") { + if (!expectedValue(actualValue)) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } else if (expectedValue instanceof RegExp) { + if (!expectedValue.test(actualValue)) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } else if (expectedValue !== actualValue) { + mismatches[key] = { actual: actualValue, expected: expectedValue }; + } + } + + if (Object.keys(mismatches).length > 0) { + const formattedMismatches = formatHeaderMismatches(mismatches); + console.error("Header mismatch:", formattedMismatches); + return passthrough(); + } + + return resolver(args); + }; +} + +function formatHeaderMismatches( + mismatches: Record boolean) }>, +): Record { + const formatted: Record = {}; + + for (const [key, { actual, expected }] of Object.entries(mismatches)) { + formatted[key] = { + actual, + expected: + expected instanceof RegExp + ? expected.toString() + : typeof expected === "function" + ? "[Function]" + : expected, + }; + } + + return formatted; +} diff --git a/tests/mock-server/withJson.ts b/tests/mock-server/withJson.ts new file mode 100644 index 00000000..bfcd9a63 --- /dev/null +++ b/tests/mock-server/withJson.ts @@ -0,0 +1,158 @@ +import { HttpResponseResolver, passthrough } from "msw"; + +import { fromJson, toJson } from "../../src/core/json"; + +/** + * Creates a request matcher that validates if the request JSON body exactly matches the expected object + * @param expectedBody - The exact body object to match against + * @param resolver - Response resolver to execute if body matches + */ +export function withJson(expectedBody: unknown, resolver: HttpResponseResolver): HttpResponseResolver { + return async (args) => { + const { request } = args; + + let clonedRequest: Request; + let bodyText: string | undefined; + let actualBody: unknown; + try { + clonedRequest = request.clone(); + bodyText = await clonedRequest.text(); + if (bodyText === "") { + console.error("Request body is empty, expected a JSON object."); + return passthrough(); + } + actualBody = fromJson(bodyText); + } catch (error) { + console.error(`Error processing request body:\n\tError: ${error}\n\tBody: ${bodyText}`); + return passthrough(); + } + + const mismatches = findMismatches(actualBody, expectedBody); + if (Object.keys(mismatches).length > 0) { + console.error("JSON body mismatch:", toJson(mismatches, undefined, 2)); + return passthrough(); + } + + return resolver(args); + }; +} + +function findMismatches(actual: any, expected: any): Record { + const mismatches: Record = {}; + + if (typeof actual !== typeof expected) { + if (areEquivalent(actual, expected)) { + return {}; + } + return { value: { actual, expected } }; + } + + if (typeof actual !== "object" || actual === null || expected === null) { + if (actual !== expected) { + if (areEquivalent(actual, expected)) { + return {}; + } + return { value: { actual, expected } }; + } + return {}; + } + + if (Array.isArray(actual) && Array.isArray(expected)) { + if (actual.length !== expected.length) { + return { length: { actual: actual.length, expected: expected.length } }; + } + + const arrayMismatches: Record = {}; + for (let i = 0; i < actual.length; i++) { + const itemMismatches = findMismatches(actual[i], expected[i]); + if (Object.keys(itemMismatches).length > 0) { + for (const [mismatchKey, mismatchValue] of Object.entries(itemMismatches)) { + arrayMismatches[`[${i}]${mismatchKey === "value" ? "" : "." + mismatchKey}`] = mismatchValue; + } + } + } + return arrayMismatches; + } + + const actualKeys = Object.keys(actual); + const expectedKeys = Object.keys(expected); + + const allKeys = new Set([...actualKeys, ...expectedKeys]); + + for (const key of allKeys) { + if (!expectedKeys.includes(key)) { + if (actual[key] === undefined) { + continue; // Skip undefined values in actual + } + mismatches[key] = { actual: actual[key], expected: undefined }; + } else if (!actualKeys.includes(key)) { + if (expected[key] === undefined) { + continue; // Skip undefined values in expected + } + mismatches[key] = { actual: undefined, expected: expected[key] }; + } else if ( + typeof actual[key] === "object" && + actual[key] !== null && + typeof expected[key] === "object" && + expected[key] !== null + ) { + const nestedMismatches = findMismatches(actual[key], expected[key]); + if (Object.keys(nestedMismatches).length > 0) { + for (const [nestedKey, nestedValue] of Object.entries(nestedMismatches)) { + mismatches[`${key}${nestedKey === "value" ? "" : "." + nestedKey}`] = nestedValue; + } + } + } else if (actual[key] !== expected[key]) { + if (areEquivalent(actual[key], expected[key])) { + continue; + } + mismatches[key] = { actual: actual[key], expected: expected[key] }; + } + } + + return mismatches; +} + +function areEquivalent(actual: unknown, expected: unknown): boolean { + if (actual === expected) { + return true; + } + if (isEquivalentBigInt(actual, expected)) { + return true; + } + if (isEquivalentDatetime(actual, expected)) { + return true; + } + return false; +} + +function isEquivalentBigInt(actual: unknown, expected: unknown) { + if (typeof actual === "number") { + actual = BigInt(actual); + } + if (typeof expected === "number") { + expected = BigInt(expected); + } + if (typeof actual === "bigint" && typeof expected === "bigint") { + return actual === expected; + } + return false; +} + +function isEquivalentDatetime(str1: unknown, str2: unknown): boolean { + if (typeof str1 !== "string" || typeof str2 !== "string") { + return false; + } + const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/; + if (!isoDatePattern.test(str1) || !isoDatePattern.test(str2)) { + return false; + } + + try { + const date1 = new Date(str1).getTime(); + const date2 = new Date(str2).getTime(); + return date1 === date2; + } catch { + return false; + } +} diff --git a/tests/phash.js b/tests/phash.js deleted file mode 100644 index 5699a3a7..00000000 --- a/tests/phash.js +++ /dev/null @@ -1,93 +0,0 @@ -import chai from "chai"; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -var imagekit = new ImageKit(initializationParams); - -// helpers -const errors = require('./helpers/errors'); -const spies = require('./helpers/spies'); - -const { expect } = chai; -const { pHashDistanceSpy } = spies; - -const failureHelper = (expectedError, ...params) => { - const { message, help } = expectedError; - const { message: error } = imagekit.pHashDistance(...params); - - expect(error).to.be.equal(`${message}: ${help}`); -}; - -const successHelper = (distance, ...params) => { - const result = imagekit.pHashDistance(...params); - - expect(result).to.be.a('number'); - expect(result).to.equal(distance); - expect(pHashDistanceSpy.calledOnceWithExactly(...params)).to.equal(true); -}; - -const pHash = { - invalidAlphabeticalString: 'INVALIDHEXSTRING', - invalidCharacterString: 'a4a655~!!@94518b', - invalidHexStringLength: '42', - numeric: 2222222222222222, - valid: 'f06830ca9f1e3e90', - // sets - dissimilar: [ - 'a4a65595ac94518b', - '7838873e791f8400', - ], - similar: [ - '2d5ad3936d2e015b', - '2d6ed293db36a4fb', - ], -}; - -describe('Utils > pHash > Distance calculator', () => { - beforeEach(() => { - pHashDistanceSpy.resetHistory(); - }); - - after(() => { - pHashDistanceSpy.resetHistory(); - }); - - context('Failure cases:', () => { - it('Should return error for missing first pHash', () => { - failureHelper(errors.MISSING_PHASH_VALUE, null, pHash.valid); - }); - - it('Should return error for missing second pHash', () => { - failureHelper(errors.MISSING_PHASH_VALUE, pHash.valid); - }); - - it('Should return error for invalid first pHash', () => { - failureHelper(errors.INVALID_PHASH_VALUE, pHash.invalidAlphabeticalString, pHash.valid); - }); - - it('Should return error for invalid second pHash', () => { - failureHelper(errors.INVALID_PHASH_VALUE, pHash.valid, pHash.invalidCharacterString); - }); - - it('Should return error for unequal pHash lengths', () => { - failureHelper(errors.UNEQUAL_STRING_LENGTH, pHash.valid, pHash.invalidHexStringLength); - }); - }); - - context('Success cases:', () => { - it('Should return zero distance between pHash for same image', () => { - successHelper(0, pHash.valid, pHash.valid); - }); - - it('Should return smaller distance between pHash for similar images', () => { - successHelper(17, pHash.similar[0], pHash.similar[1]); - }); - - it('Should return larger distance between pHash for dissimilar images', () => { - successHelper(37, pHash.dissimilar[0], pHash.dissimilar[1]); - }); - - it('Should return distance for non-string but valid hexanumeric pHash', () => { - successHelper(30, pHash.valid, pHash.numeric); - }); - }); -}); \ No newline at end of file diff --git a/tests/response-metadata.js b/tests/response-metadata.js deleted file mode 100644 index 6ca30796..00000000 --- a/tests/response-metadata.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Only checking for one API success and error. Assuing that all API uses same underlying request util - */ - -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams - -import ImageKit from "../index"; -import nock from "nock"; -var imagekit = new ImageKit(initializationParams); - -const dummyAPISuccessResponse = { - dummyKey: "dummyValue" -}; - -const dummyAPIErrorResponse = { - help: "help", - message: "message" -} - -const dummyAPIErrorResponseString = "Internal server error" - -const responseHeaders = { - 'x-request-id': "request-id" -} - -describe("Promise", function () { - it('Success', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, dummyAPISuccessResponse, responseHeaders) - - var response = await imagekit.getPurgeCacheStatus(requestId); - expect(response).to.be.deep.equal(dummyAPISuccessResponse); - expect(response.$ResponseMetadata.statusCode).to.be.equal(200); - expect(response.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders, - 'content-type': 'application/json' - }); - return Promise.resolve(); - }); - - it('Server handled error', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponse, responseHeaders) - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch (ex) { - expect(ex).to.be.deep.equal(dummyAPIErrorResponse); - expect(ex.$ResponseMetadata.statusCode).to.be.equal(500); - expect(ex.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders, - 'content-type': 'application/json' - }); - return Promise.resolve(); - } - - return Promise.reject(); - }); - - it('Server unhandled error', async function () { - var requestId = "sdfdsfksjfldsjfjsdf"; - - const scope = nock('https://api.imagekit.io') - .get(`/v1/files/purge/${requestId}`) - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(500, dummyAPIErrorResponseString, responseHeaders) - - try { - await imagekit.getPurgeCacheStatus(requestId); - } catch (ex) { - expect(ex).to.be.deep.equal({ - help: dummyAPIErrorResponseString - }); - expect(ex.$ResponseMetadata.statusCode).to.be.equal(500); - expect(ex.$ResponseMetadata.headers).to.be.deep.equal({ - ...responseHeaders - }); - return Promise.resolve(); - } - - return Promise.reject(); - }); -}); \ No newline at end of file diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 00000000..10185ed2 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": null, + "rootDir": "..", + "baseUrl": ".." + }, + "include": ["../src", "../tests"], + "exclude": [] +} diff --git a/tests/unit.js b/tests/unit.js deleted file mode 100644 index 61be7331..00000000 --- a/tests/unit.js +++ /dev/null @@ -1,78 +0,0 @@ -import chai from "chai"; -const expect = chai.expect; -import ImageKit from "../index"; - -import urlBuilder from "../libs/url/builder"; - -describe("Unit test cases", function () { - var imagekit = new ImageKit({ - publicKey: "public_key_test", - privateKey: "private_key_test", - urlEndpoint: "https://test-domain.com/test-endpoint" - }); - - it('Authentication params check', function () { - var authenticationParameters = imagekit.getAuthenticationParameters("your_token", 1582269249); - expect(authenticationParameters).to.deep.equal({ - token: 'your_token', - expire: 1582269249, - signature: 'e71bcd6031016b060d349d212e23e85c791decdd' - }) - }); - - it('Authentication params check no params', function () { - var authenticationParameters = imagekit.getAuthenticationParameters(); - expect(authenticationParameters).to.have.property("token"); - expect(authenticationParameters).to.have.property("expire"); - expect(authenticationParameters).to.have.property("signature"); - }); - - it('Signed URL signature without slash default expiry', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - privateKey: "private_key_test", - url: url, - urlEndpoint:"https://test-domain.com/test-endpoint", - expiryTimestamp: "9999999999" - }) - expect(signature).to.be.equal("41b3075c40bc84147eb71b8b49ae7fbf349d0f00") - }); - - it('Signed URL signature with slash default expiry', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - privateKey: "private_key_test", - url: url, - urlEndpoint:"https://test-domain.com/test-endpoint/", - expiryTimestamp: "9999999999" - }) - expect(signature).to.be.equal("41b3075c40bc84147eb71b8b49ae7fbf349d0f00") - }); - - it('Signed URL signature empty', function () { - var url = "https://test-domain.com/test-endpoint/tr:w-100/test-signed-url.png"; - var signature = urlBuilder.getSignature({ - }) - expect(signature).to.be.equal("") - }); - - it('pHash distance different', function () { - var pHashDistance = imagekit.pHashDistance("33699c96619cc69e","968e978414fe04ea"); - expect(pHashDistance).to.be.equal(30) - }); - - it('pHash distance similar', function () { - var pHashDistance = imagekit.pHashDistance("63433b3ccf8e1ebe","f5d2226cd9d32b16"); - expect(pHashDistance).to.be.equal(27) - }); - - it('pHash distance similar reverse', function () { - var pHashDistance = imagekit.pHashDistance("f5d2226cd9d32b16","63433b3ccf8e1ebe"); - expect(pHashDistance).to.be.equal(27) - }); - - it('pHash distance same', function () { - var pHashDistance = imagekit.pHashDistance("33699c96619cc69e","33699c96619cc69e"); - expect(pHashDistance).to.be.equal(0) - }); -}); \ No newline at end of file diff --git a/tests/unit/auth/BasicAuth.test.ts b/tests/unit/auth/BasicAuth.test.ts new file mode 100644 index 00000000..79ef9799 --- /dev/null +++ b/tests/unit/auth/BasicAuth.test.ts @@ -0,0 +1,22 @@ +import { BasicAuth } from "../../../src/core/auth/BasicAuth"; + +describe("BasicAuth", () => { + describe("toAuthorizationHeader", () => { + it("correctly converts to header", () => { + expect( + BasicAuth.toAuthorizationHeader({ + username: "username", + password: "password", + }), + ).toBe("Basic dXNlcm5hbWU6cGFzc3dvcmQ="); + }); + }); + describe("fromAuthorizationHeader", () => { + it("correctly parses header", () => { + expect(BasicAuth.fromAuthorizationHeader("Basic dXNlcm5hbWU6cGFzc3dvcmQ=")).toEqual({ + username: "username", + password: "password", + }); + }); + }); +}); diff --git a/tests/unit/auth/BearerToken.test.ts b/tests/unit/auth/BearerToken.test.ts new file mode 100644 index 00000000..7757b87c --- /dev/null +++ b/tests/unit/auth/BearerToken.test.ts @@ -0,0 +1,14 @@ +import { BearerToken } from "../../../src/core/auth/BearerToken"; + +describe("BearerToken", () => { + describe("toAuthorizationHeader", () => { + it("correctly converts to header", () => { + expect(BearerToken.toAuthorizationHeader("my-token")).toBe("Bearer my-token"); + }); + }); + describe("fromAuthorizationHeader", () => { + it("correctly parses header", () => { + expect(BearerToken.fromAuthorizationHeader("Bearer my-token")).toBe("my-token"); + }); + }); +}); diff --git a/tests/unit/base64.test.ts b/tests/unit/base64.test.ts new file mode 100644 index 00000000..939594ca --- /dev/null +++ b/tests/unit/base64.test.ts @@ -0,0 +1,53 @@ +import { base64Decode, base64Encode } from "../../src/core/base64"; + +describe("base64", () => { + describe("base64Encode", () => { + it("should encode ASCII strings", () => { + expect(base64Encode("hello")).toBe("aGVsbG8="); + expect(base64Encode("")).toBe(""); + }); + + it("should encode UTF-8 strings", () => { + expect(base64Encode("café")).toBe("Y2Fmw6k="); + expect(base64Encode("🎉")).toBe("8J+OiQ=="); + }); + + it("should handle basic auth credentials", () => { + expect(base64Encode("username:password")).toBe("dXNlcm5hbWU6cGFzc3dvcmQ="); + }); + }); + + describe("base64Decode", () => { + it("should decode ASCII strings", () => { + expect(base64Decode("aGVsbG8=")).toBe("hello"); + expect(base64Decode("")).toBe(""); + }); + + it("should decode UTF-8 strings", () => { + expect(base64Decode("Y2Fmw6k=")).toBe("café"); + expect(base64Decode("8J+OiQ==")).toBe("🎉"); + }); + + it("should handle basic auth credentials", () => { + expect(base64Decode("dXNlcm5hbWU6cGFzc3dvcmQ=")).toBe("username:password"); + }); + }); + + describe("round-trip encoding", () => { + const testStrings = [ + "hello world", + "test@example.com", + "café", + "username:password", + "user@domain.com:super$ecret123!", + ]; + + testStrings.forEach((testString) => { + it(`should round-trip encode/decode: "${testString}"`, () => { + const encoded = base64Encode(testString); + const decoded = base64Decode(encoded); + expect(decoded).toBe(testString); + }); + }); + }); +}); diff --git a/tests/unit/fetcher/Fetcher.test.ts b/tests/unit/fetcher/Fetcher.test.ts new file mode 100644 index 00000000..f983f08a --- /dev/null +++ b/tests/unit/fetcher/Fetcher.test.ts @@ -0,0 +1,256 @@ +import fs from "fs"; +import stream from "stream"; +import { join } from "path"; + +import { Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; +import type { BinaryResponse } from "../../../src/core"; + +describe("Test fetcherImpl", () => { + it("should handle successful request", async () => { + const mockArgs: Fetcher.Args = { + url: "https://httpbin.org/post", + method: "POST", + headers: { "X-Test": "x-test-header" }, + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + }), + ); + + const result = await fetcherImpl(mockArgs); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + + expect(global.fetch).toHaveBeenCalledWith( + "https://httpbin.org/post", + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + body: JSON.stringify({ data: "test" }), + }), + ); + }); + + it("should send octet stream", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "POST", + headers: { "X-Test": "x-test-header" }, + contentType: "application/octet-stream", + requestType: "bytes", + responseType: "json", + body: fs.createReadStream(join(__dirname, "test-file.txt")), + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + }), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + body: expect.any(fs.ReadStream), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + }); + + it("should receive file as stream", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + responseType: "binary-response", + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.stream).toBe("function"); + const stream = body.stream(); + expect(stream).toBeInstanceOf(ReadableStream); + const reader = stream.getReader(); + const { value } = await reader.read(); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(value); + expect(streamContent).toBe("This is a test file!\n"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as blob", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + responseType: "binary-response", + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.blob).toBe("function"); + const blob = await body.blob(); + expect(blob).toBeInstanceOf(Blob); + const reader = blob.stream().getReader(); + const { value } = await reader.read(); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(value); + expect(streamContent).toBe("This is a test file!\n"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as arraybuffer", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + responseType: "binary-response", + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.arrayBuffer).toBe("function"); + const arrayBuffer = await body.arrayBuffer(); + expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(new Uint8Array(arrayBuffer)); + expect(streamContent).toBe("This is a test file!\n"); + expect(body.bodyUsed).toBe(true); + } + }); + + it("should receive file as bytes", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "GET", + headers: { "X-Test": "x-test-header" }, + responseType: "binary-response", + }; + + global.fetch = jest.fn().mockResolvedValue( + new Response( + stream.Readable.toWeb(fs.createReadStream(join(__dirname, "test-file.txt"))) as ReadableStream, + { + status: 200, + statusText: "OK", + }, + ), + ); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "GET", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + }), + ); + expect(result.ok).toBe(true); + if (result.ok) { + const body = result.body as BinaryResponse; + expect(body).toBeDefined(); + expect(body.bodyUsed).toBe(false); + expect(typeof body.bytes).toBe("function"); + if (!body.bytes) { + return; + } + const bytes = await body.bytes(); + expect(bytes).toBeInstanceOf(Uint8Array); + const decoder = new TextDecoder(); + const streamContent = decoder.decode(bytes); + expect(streamContent).toBe("This is a test file!\n"); + expect(body.bodyUsed).toBe(true); + } + }); +}); diff --git a/tests/unit/fetcher/HttpResponsePromise.test.ts b/tests/unit/fetcher/HttpResponsePromise.test.ts new file mode 100644 index 00000000..2216a33e --- /dev/null +++ b/tests/unit/fetcher/HttpResponsePromise.test.ts @@ -0,0 +1,143 @@ +import { beforeEach, describe, expect, it, jest } from "@jest/globals"; + +import { HttpResponsePromise } from "../../../src/core/fetcher/HttpResponsePromise"; +import { RawResponse, WithRawResponse } from "../../../src/core/fetcher/RawResponse"; + +describe("HttpResponsePromise", () => { + const mockRawResponse: RawResponse = { + headers: new Headers(), + redirected: false, + status: 200, + statusText: "OK", + type: "basic" as ResponseType, + url: "https://example.com", + }; + const mockData = { id: "123", name: "test" }; + const mockWithRawResponse: WithRawResponse = { + data: mockData, + rawResponse: mockRawResponse, + }; + + describe("fromFunction", () => { + it("should create an HttpResponsePromise from a function", async () => { + const mockFn = jest + .fn<(arg1: string, arg2: string) => Promise>>() + .mockResolvedValue(mockWithRawResponse); + + const responsePromise = HttpResponsePromise.fromFunction(mockFn, "arg1", "arg2"); + + const result = await responsePromise; + expect(result).toEqual(mockData); + expect(mockFn).toHaveBeenCalledWith("arg1", "arg2"); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromPromise", () => { + it("should create an HttpResponsePromise from a promise", async () => { + const promise = Promise.resolve(mockWithRawResponse); + + const responsePromise = HttpResponsePromise.fromPromise(promise); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromExecutor", () => { + it("should create an HttpResponsePromise from an executor function", async () => { + const responsePromise = HttpResponsePromise.fromExecutor((resolve) => { + resolve(mockWithRawResponse); + }); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("fromResult", () => { + it("should create an HttpResponsePromise from a result", async () => { + const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + + const result = await responsePromise; + expect(result).toEqual(mockData); + + const resultWithRawResponse = await responsePromise.withRawResponse(); + expect(resultWithRawResponse).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); + + describe("Promise methods", () => { + let responsePromise: HttpResponsePromise; + + beforeEach(() => { + responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + }); + + it("should support then() method", async () => { + const result = await responsePromise.then((data) => ({ + ...data, + modified: true, + })); + + expect(result).toEqual({ + ...mockData, + modified: true, + }); + }); + + it("should support catch() method", async () => { + const errorResponsePromise = HttpResponsePromise.fromExecutor((_, reject) => { + reject(new Error("Test error")); + }); + + const catchSpy = jest.fn(); + await errorResponsePromise.catch(catchSpy); + + expect(catchSpy).toHaveBeenCalled(); + const error = catchSpy.mock.calls[0]?.[0]; + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe("Test error"); + }); + + it("should support finally() method", async () => { + const finallySpy = jest.fn(); + await responsePromise.finally(finallySpy); + + expect(finallySpy).toHaveBeenCalled(); + }); + }); + + describe("withRawResponse", () => { + it("should return both data and raw response", async () => { + const responsePromise = HttpResponsePromise.fromResult(mockWithRawResponse); + + const result = await responsePromise.withRawResponse(); + + expect(result).toEqual({ + data: mockData, + rawResponse: mockRawResponse, + }); + }); + }); +}); diff --git a/tests/unit/fetcher/RawResponse.test.ts b/tests/unit/fetcher/RawResponse.test.ts new file mode 100644 index 00000000..9ccd5e1e --- /dev/null +++ b/tests/unit/fetcher/RawResponse.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "@jest/globals"; + +import { toRawResponse } from "../../../src/core/fetcher/RawResponse"; + +describe("RawResponse", () => { + describe("toRawResponse", () => { + it("should convert Response to RawResponse by removing body, bodyUsed, and ok properties", () => { + const mockHeaders = new Headers({ "content-type": "application/json" }); + const mockResponse = { + body: "test body", + bodyUsed: false, + ok: true, + headers: mockHeaders, + redirected: false, + status: 200, + statusText: "OK", + type: "basic" as ResponseType, + url: "https://example.com", + }; + + const result = toRawResponse(mockResponse as unknown as Response); + + expect("body" in result).toBe(false); + expect("bodyUsed" in result).toBe(false); + expect("ok" in result).toBe(false); + expect(result.headers).toBe(mockHeaders); + expect(result.redirected).toBe(false); + expect(result.status).toBe(200); + expect(result.statusText).toBe("OK"); + expect(result.type).toBe("basic"); + expect(result.url).toBe("https://example.com"); + }); + }); +}); diff --git a/tests/unit/fetcher/createRequestUrl.test.ts b/tests/unit/fetcher/createRequestUrl.test.ts new file mode 100644 index 00000000..06e03b2c --- /dev/null +++ b/tests/unit/fetcher/createRequestUrl.test.ts @@ -0,0 +1,160 @@ +import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; + +describe("Test createRequestUrl", () => { + it("should return the base URL when no query parameters are provided", () => { + const baseUrl = "https://api.example.com"; + expect(createRequestUrl(baseUrl)).toBe(baseUrl); + }); + + it("should append simple query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { key: "value", another: "param" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?key=value&another=param"); + }); + + it("should handle array query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { items: ["a", "b", "c"] }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?items=a&items=b&items=c"); + }); + + it("should handle object query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { filter: { name: "John", age: 30 } }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", + ); + }); + + it("should handle mixed types of query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + simple: "value", + array: ["x", "y"], + object: { key: "value" }, + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", + ); + }); + + it("should handle empty query parameters object", () => { + const baseUrl = "https://api.example.com"; + expect(createRequestUrl(baseUrl, {})).toBe(baseUrl); + }); + + it("should encode special characters in query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { special: "a&b=c d" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?special=a%26b%3Dc%20d"); + }); + + // Additional tests for edge cases and different value types + it("should handle numeric values", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { count: 42, price: 19.99, active: 1, inactive: 0 }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?count=42&price=19.99&active=1&inactive=0", + ); + }); + + it("should handle boolean values", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { enabled: true, disabled: false }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?enabled=true&disabled=false"); + }); + + it("should handle null and undefined values", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + valid: "value", + nullValue: null, + undefinedValue: undefined, + emptyString: "", + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?valid=value&nullValue=&emptyString=", + ); + }); + + it("should handle deeply nested objects", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + user: { + profile: { + name: "John", + settings: { theme: "dark" }, + }, + }, + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + ); + }); + + it("should handle arrays of objects", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + users: [ + { name: "John", age: 30 }, + { name: "Jane", age: 25 }, + ], + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", + ); + }); + + it("should handle mixed arrays", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + mixed: ["string", 42, true, { key: "value" }], + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", + ); + }); + + it("should handle empty arrays", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { emptyArray: [] }; + expect(createRequestUrl(baseUrl, queryParams)).toBe(baseUrl); + }); + + it("should handle empty objects", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { emptyObject: {} }; + expect(createRequestUrl(baseUrl, queryParams)).toBe(baseUrl); + }); + + it("should handle special characters in keys", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { "key with spaces": "value", "key[with]brackets": "value" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", + ); + }); + + it("should handle URL with existing query parameters", () => { + const baseUrl = "https://api.example.com?existing=param"; + const queryParams = { new: "value" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?existing=param?new=value"); + }); + + it("should handle complex nested structures", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + ); + }); +}); diff --git a/tests/unit/fetcher/getRequestBody.test.ts b/tests/unit/fetcher/getRequestBody.test.ts new file mode 100644 index 00000000..e864c8b5 --- /dev/null +++ b/tests/unit/fetcher/getRequestBody.test.ts @@ -0,0 +1,65 @@ +import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; +import { RUNTIME } from "../../../src/core/runtime"; + +describe("Test getRequestBody", () => { + it("should stringify body if not FormData in Node environment", async () => { + if (RUNTIME.type === "node") { + const body = { key: "value" }; + const result = await getRequestBody({ + body, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + } + }); + + it("should return FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const formData = new FormData(); + formData.append("key", "value"); + const result = await getRequestBody({ + body: formData, + type: "file", + }); + expect(result).toBe(formData); + } + }); + + it("should stringify body if not FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const body = { key: "value" }; + const result = await getRequestBody({ + body, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + } + }); + + it("should return the Uint8Array", async () => { + const input = new Uint8Array([1, 2, 3]); + const result = await getRequestBody({ + body: input, + type: "bytes", + }); + expect(result).toBe(input); + }); + + it("should return the input for content-type 'application/x-www-form-urlencoded'", async () => { + const input = "key=value&another=param"; + const result = await getRequestBody({ + body: input, + type: "other", + }); + expect(result).toBe(input); + }); + + it("should JSON stringify objects", async () => { + const input = { key: "value" }; + const result = await getRequestBody({ + body: input, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + }); +}); diff --git a/tests/unit/fetcher/getResponseBody.test.ts b/tests/unit/fetcher/getResponseBody.test.ts new file mode 100644 index 00000000..400782f5 --- /dev/null +++ b/tests/unit/fetcher/getResponseBody.test.ts @@ -0,0 +1,77 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; + +describe("Test getResponseBody", () => { + it("should handle blob response type", async () => { + const mockBlob = new Blob(["test"], { type: "text/plain" }); + const mockResponse = new Response(mockBlob); + const result = await getResponseBody(mockResponse, "blob"); + // @ts-expect-error + expect(result.constructor.name).toBe("Blob"); + }); + + it("should handle sse response type", async () => { + if (RUNTIME.type === "node") { + const mockStream = new ReadableStream(); + const mockResponse = new Response(mockStream); + const result = await getResponseBody(mockResponse, "sse"); + expect(result).toBe(mockStream); + } + }); + + it("should handle streaming response type", async () => { + // Create a ReadableStream with some test data + const encoder = new TextEncoder(); + const testData = "test stream data"; + const mockStream = new ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode(testData)); + controller.close(); + }, + }); + + const mockResponse = new Response(mockStream); + const result = (await getResponseBody(mockResponse, "streaming")) as ReadableStream; + + expect(result).toBeInstanceOf(ReadableStream); + + // Read and verify the stream content + const reader = result.getReader(); + const decoder = new TextDecoder(); + const { value } = await reader.read(); + const streamContent = decoder.decode(value); + expect(streamContent).toBe(testData); + }); + + it("should handle text response type", async () => { + const mockResponse = new Response("test text"); + const result = await getResponseBody(mockResponse, "text"); + expect(result).toBe("test text"); + }); + + it("should handle JSON response", async () => { + const mockJson = { key: "value" }; + const mockResponse = new Response(JSON.stringify(mockJson)); + const result = await getResponseBody(mockResponse); + expect(result).toEqual(mockJson); + }); + + it("should handle empty response", async () => { + const mockResponse = new Response(""); + const result = await getResponseBody(mockResponse); + expect(result).toBeUndefined(); + }); + + it("should handle non-JSON response", async () => { + const mockResponse = new Response("invalid json"); + const result = await getResponseBody(mockResponse); + expect(result).toEqual({ + ok: false, + error: { + reason: "non-json", + statusCode: 200, + rawBody: "invalid json", + }, + }); + }); +}); diff --git a/tests/unit/fetcher/makeRequest.test.ts b/tests/unit/fetcher/makeRequest.test.ts new file mode 100644 index 00000000..43ed9d11 --- /dev/null +++ b/tests/unit/fetcher/makeRequest.test.ts @@ -0,0 +1,53 @@ +import { makeRequest } from "../../../src/core/fetcher/makeRequest"; + +describe("Test makeRequest", () => { + const mockPostUrl = "https://httpbin.org/post"; + const mockGetUrl = "https://httpbin.org/get"; + const mockHeaders = { "Content-Type": "application/json" }; + const mockBody = JSON.stringify({ key: "value" }); + + let mockFetch: jest.Mock; + + beforeEach(() => { + mockFetch = jest.fn(); + mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); + }); + + it("should handle POST request correctly", async () => { + const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockPostUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "POST", + headers: mockHeaders, + body: mockBody, + credentials: undefined, + }), + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); + + it("should handle GET request correctly", async () => { + const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockGetUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "GET", + headers: mockHeaders, + body: undefined, + credentials: undefined, + }), + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); +}); diff --git a/tests/unit/fetcher/requestWithRetries.test.ts b/tests/unit/fetcher/requestWithRetries.test.ts new file mode 100644 index 00000000..3cdaa40a --- /dev/null +++ b/tests/unit/fetcher/requestWithRetries.test.ts @@ -0,0 +1,132 @@ +import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; + +describe("requestWithRetries", () => { + let mockFetch: jest.Mock; + let originalMathRandom: typeof Math.random; + let setTimeoutSpy: jest.SpyInstance; + + beforeEach(() => { + mockFetch = jest.fn(); + originalMathRandom = Math.random; + + // Mock Math.random for consistent jitter + Math.random = jest.fn(() => 0.5); + + jest.useFakeTimers({ doNotFake: ["nextTick"] }); + }); + + afterEach(() => { + Math.random = originalMathRandom; + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + it("should retry on retryable status codes", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const retryableStatuses = [408, 429, 500, 502]; + let callCount = 0; + + mockFetch.mockImplementation(async () => { + if (callCount < retryableStatuses.length) { + return new Response("", { status: retryableStatuses[callCount++] }); + } + return new Response("", { status: 200 }); + }); + + const responsePromise = requestWithRetries(() => mockFetch(), retryableStatuses.length); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(retryableStatuses.length + 1); + expect(response.status).toBe(200); + }); + + it("should respect maxRetries limit", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const maxRetries = 2; + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + expect(response.status).toBe(500); + }); + + it("should not retry on success status codes", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const successStatuses = [200, 201, 202]; + + for (const status of successStatuses) { + mockFetch.mockReset(); + setTimeoutSpy.mockClear(); + mockFetch.mockResolvedValueOnce(new Response("", { status })); + + const responsePromise = requestWithRetries(() => mockFetch(), 3); + await jest.runAllTimersAsync(); + await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(1); + expect(setTimeoutSpy).not.toHaveBeenCalled(); + } + }); + + it("should apply correct exponential backoff with jitter", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + const maxRetries = 3; + const expectedDelays = [1000, 2000, 4000]; + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await jest.runAllTimersAsync(); + await responsePromise; + + // Verify setTimeout calls + expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); + + expectedDelays.forEach((delay, index) => { + expect(setTimeoutSpy).toHaveBeenNthCalledWith(index + 1, expect.any(Function), delay); + }); + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + }); + + it("should handle concurrent retries independently", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 200 })) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const promise1 = requestWithRetries(() => mockFetch(), 1); + const promise2 = requestWithRetries(() => mockFetch(), 1); + + await jest.runAllTimersAsync(); + const [response1, response2] = await Promise.all([promise1, promise2]); + + expect(response1.status).toBe(200); + expect(response2.status).toBe(200); + }); +}); diff --git a/tests/unit/fetcher/signals.test.ts b/tests/unit/fetcher/signals.test.ts new file mode 100644 index 00000000..9cabfa07 --- /dev/null +++ b/tests/unit/fetcher/signals.test.ts @@ -0,0 +1,69 @@ +import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; + +describe("Test getTimeoutSignal", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("should return an object with signal and abortId", () => { + const { signal, abortId } = getTimeoutSignal(1000); + + expect(signal).toBeDefined(); + expect(abortId).toBeDefined(); + expect(signal).toBeInstanceOf(AbortSignal); + expect(signal.aborted).toBe(false); + }); + + it("should create a signal that aborts after the specified timeout", () => { + const timeoutMs = 5000; + const { signal } = getTimeoutSignal(timeoutMs); + + expect(signal.aborted).toBe(false); + + jest.advanceTimersByTime(timeoutMs - 1); + expect(signal.aborted).toBe(false); + + jest.advanceTimersByTime(1); + expect(signal.aborted).toBe(true); + }); +}); + +describe("Test anySignal", () => { + it("should return an AbortSignal", () => { + const signal = anySignal(new AbortController().signal); + expect(signal).toBeInstanceOf(AbortSignal); + }); + + it("should abort when any of the input signals is aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal(controller1.signal, controller2.signal); + + expect(signal.aborted).toBe(false); + controller1.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should handle an array of signals", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal([controller1.signal, controller2.signal]); + + expect(signal.aborted).toBe(false); + controller2.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should abort immediately if one of the input signals is already aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + controller1.abort(); + + const signal = anySignal(controller1.signal, controller2.signal); + expect(signal.aborted).toBe(true); + }); +}); diff --git a/tests/unit/fetcher/test-file.txt b/tests/unit/fetcher/test-file.txt new file mode 100644 index 00000000..c66d471e --- /dev/null +++ b/tests/unit/fetcher/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/tests/unit/file/file.test.ts b/tests/unit/file/file.test.ts new file mode 100644 index 00000000..0bc7c879 --- /dev/null +++ b/tests/unit/file/file.test.ts @@ -0,0 +1,540 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "test-file.txt"); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": "21", // Should determine from file system (test file is 21 bytes) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": "21", // Should determine from file system (test file is 21 bytes) + }); + }); + + it("should handle Windows-style paths", async () => { + const input: Uploadable.FromPath = { + path: "C:\\Users\\test\\file.txt", + }; + + // Mock fs methods to avoid actual file system access + const mockStats = { size: 123 }; + const mockReadStream = {} as fs.ReadStream; + + const createReadStreamSpy = jest.spyOn(fs, "createReadStream").mockReturnValue(mockReadStream); + const statSpy = jest.spyOn(fs.promises, "stat").mockResolvedValue(mockStats as fs.Stats); + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(mockReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="file.txt"', // Should extract from Windows path + "Content-Length": "123", + }); + + // Restore mocks + createReadStreamSpy.mockRestore(); + statSpy.mockRestore(); + }); + + it("should handle file path when fs is not available", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + // Mock import to simulate environment without fs + const originalImport = jest.requireActual("fs"); + jest.doMock("fs", () => null); + + await expect(toBinaryUploadRequest(input)).rejects.toThrow( + "File path uploads are not supported in this environment.", + ); + + // Restore fs + jest.doMock("fs", () => originalImport); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/tests/unit/file/test-file.txt b/tests/unit/file/test-file.txt new file mode 100644 index 00000000..c66d471e --- /dev/null +++ b/tests/unit/file/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/tests/unit/form-data-utils/encodeAsFormParameter.test.ts b/tests/unit/form-data-utils/encodeAsFormParameter.test.ts new file mode 100644 index 00000000..d4b0c450 --- /dev/null +++ b/tests/unit/form-data-utils/encodeAsFormParameter.test.ts @@ -0,0 +1,344 @@ +import { encodeAsFormParameter } from "../../../src/core/form-data-utils/encodeAsFormParameter"; + +describe("encodeAsFormParameter", () => { + describe("Basic functionality", () => { + it("should return empty object for null/undefined", () => { + expect(encodeAsFormParameter(null)).toEqual({}); + expect(encodeAsFormParameter(undefined)).toEqual({}); + }); + + it("should return empty object for primitive values", () => { + expect(encodeAsFormParameter("hello")).toEqual({}); + expect(encodeAsFormParameter(42)).toEqual({}); + expect(encodeAsFormParameter(true)).toEqual({}); + }); + + it("should handle simple key-value pairs", () => { + const obj = { name: "John", age: 30 }; + expect(encodeAsFormParameter(obj)).toEqual({ + name: "John", + age: "30", + }); + }); + + it("should handle empty objects", () => { + expect(encodeAsFormParameter({})).toEqual({}); + }); + }); + + describe("Array handling", () => { + it("should handle arrays with indices format (default)", () => { + const obj = { items: ["a", "b", "c"] }; + expect(encodeAsFormParameter(obj)).toEqual({ + "items[0]": "a", + "items[1]": "b", + "items[2]": "c", + }); + }); + + it("should handle empty arrays", () => { + const obj = { items: [] }; + expect(encodeAsFormParameter(obj)).toEqual({}); + }); + + it("should handle arrays with mixed types", () => { + const obj = { mixed: ["string", 42, true, false] }; + expect(encodeAsFormParameter(obj)).toEqual({ + "mixed[0]": "string", + "mixed[1]": "42", + "mixed[2]": "true", + "mixed[3]": "false", + }); + }); + + it("should handle arrays with objects", () => { + const obj = { users: [{ name: "John" }, { name: "Jane" }] }; + expect(encodeAsFormParameter(obj)).toEqual({ + "users[0][name]": "John", + "users[1][name]": "Jane", + }); + }); + + it("should handle arrays with null/undefined values", () => { + const obj = { items: ["a", null, "c", undefined, "e"] }; + expect(encodeAsFormParameter(obj)).toEqual({ + "items[0]": "a", + "items[1]": "", + "items[2]": "c", + "items[4]": "e", + }); + }); + }); + + describe("Nested objects", () => { + it("should handle nested objects", () => { + const obj = { user: { name: "John", age: 30 } }; + expect(encodeAsFormParameter(obj)).toEqual({ + "user[name]": "John", + "user[age]": "30", + }); + }); + + it("should handle deeply nested objects", () => { + const obj = { user: { profile: { name: "John", settings: { theme: "dark" } } } }; + expect(encodeAsFormParameter(obj)).toEqual({ + "user[profile][name]": "John", + "user[profile][settings][theme]": "dark", + }); + }); + + it("should handle empty nested objects", () => { + const obj = { user: {} }; + expect(encodeAsFormParameter(obj)).toEqual({}); + }); + }); + + describe("Special characters and encoding", () => { + it("should not encode values (encode: false is used)", () => { + const obj = { name: "John Doe", email: "john@example.com" }; + expect(encodeAsFormParameter(obj)).toEqual({ + name: "John Doe", + email: "john@example.com", + }); + }); + + it("should not encode special characters in keys", () => { + const obj = { "user name": "John", "email[primary]": "john@example.com" }; + expect(encodeAsFormParameter(obj)).toEqual({ + "user name": "John", + "email[primary]": "john@example.com", + }); + }); + + it("should handle values that contain special characters", () => { + const obj = { + query: "search term with spaces", + filter: "category:electronics", + }; + expect(encodeAsFormParameter(obj)).toEqual({ + query: "search term with spaces", + filter: "category:electronics", + }); + }); + + it("should handle ampersand and equals characters (edge case)", () => { + // Note: Values containing & and = may be problematic because + // encodeAsFormParameter splits on these characters when parsing the stringified result + const obj = { + message: "Hello & welcome", + equation: "x = y + z", + }; + // This demonstrates the limitation - ampersands and equals signs in values + // will cause the parameter to be split incorrectly + const result = encodeAsFormParameter(obj); + + // We expect this to be parsed incorrectly due to the implementation + expect(result.message).toBe("Hello "); + expect(result[" welcome"]).toBeUndefined(); + expect(result.equation).toBe("x "); + expect(result[" y + z"]).toBeUndefined(); + }); + }); + + describe("Form data specific scenarios", () => { + it("should handle file upload metadata", () => { + const metadata = { + file: { + name: "document.pdf", + size: 1024, + type: "application/pdf", + }, + options: { + compress: true, + quality: 0.8, + }, + }; + expect(encodeAsFormParameter(metadata)).toEqual({ + "file[name]": "document.pdf", + "file[size]": "1024", + "file[type]": "application/pdf", + "options[compress]": "true", + "options[quality]": "0.8", + }); + }); + + it("should handle form validation data", () => { + const formData = { + fields: ["name", "email", "phone"], + validation: { + required: ["name", "email"], + patterns: { + email: "^[^@]+@[^@]+\\.[^@]+$", + phone: "^\\+?[1-9]\\d{1,14}$", + }, + }, + }; + expect(encodeAsFormParameter(formData)).toEqual({ + "fields[0]": "name", + "fields[1]": "email", + "fields[2]": "phone", + "validation[required][0]": "name", + "validation[required][1]": "email", + "validation[patterns][email]": "^[^@]+@[^@]+\\.[^@]+$", + "validation[patterns][phone]": "^\\+?[1-9]\\d{1,14}$", + }); + }); + + it("should handle search/filter parameters", () => { + const searchParams = { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + pagination: { page: 1, limit: 20 }, + }; + expect(encodeAsFormParameter(searchParams)).toEqual({ + "filters[status][0]": "active", + "filters[status][1]": "pending", + "filters[category][type]": "electronics", + "filters[category][subcategories][0]": "phones", + "filters[category][subcategories][1]": "laptops", + "sort[field]": "name", + "sort[direction]": "asc", + "pagination[page]": "1", + "pagination[limit]": "20", + }); + }); + }); + + describe("Edge cases", () => { + it("should handle boolean values", () => { + const obj = { enabled: true, disabled: false }; + expect(encodeAsFormParameter(obj)).toEqual({ + enabled: "true", + disabled: "false", + }); + }); + + it("should handle empty strings", () => { + const obj = { name: "", description: "test" }; + expect(encodeAsFormParameter(obj)).toEqual({ + name: "", + description: "test", + }); + }); + + it("should handle zero values", () => { + const obj = { count: 0, price: 0.0 }; + expect(encodeAsFormParameter(obj)).toEqual({ + count: "0", + price: "0", + }); + }); + + it("should handle numeric keys", () => { + const obj = { "0": "zero", "1": "one" }; + expect(encodeAsFormParameter(obj)).toEqual({ + "0": "zero", + "1": "one", + }); + }); + + it("should handle objects with null/undefined values", () => { + const obj = { name: "John", age: null, email: undefined, active: true }; + expect(encodeAsFormParameter(obj)).toEqual({ + name: "John", + age: "", + active: "true", + }); + }); + }); + + describe("Integration with form submission", () => { + it("should produce form-compatible key-value pairs", () => { + const formObject = { + username: "john_doe", + preferences: { + theme: "dark", + notifications: ["email", "push"], + settings: { + autoSave: true, + timeout: 300, + }, + }, + }; + + const result = encodeAsFormParameter(formObject); + + // Verify all values are strings (as required for form data) + Object.values(result).forEach((value) => { + expect(typeof value).toBe("string"); + }); + + // Verify the structure can be reconstructed + expect(result).toEqual({ + username: "john_doe", + "preferences[theme]": "dark", + "preferences[notifications][0]": "email", + "preferences[notifications][1]": "push", + "preferences[settings][autoSave]": "true", + "preferences[settings][timeout]": "300", + }); + }); + + it("should handle complex nested arrays for API parameters", () => { + const apiParams = { + query: { + filters: [ + { field: "status", operator: "eq", value: "active" }, + { field: "created", operator: "gte", value: "2023-01-01" }, + ], + sort: [ + { field: "name", direction: "asc" }, + { field: "created", direction: "desc" }, + ], + }, + }; + + const result = encodeAsFormParameter(apiParams); + expect(result).toEqual({ + "query[filters][0][field]": "status", + "query[filters][0][operator]": "eq", + "query[filters][0][value]": "active", + "query[filters][1][field]": "created", + "query[filters][1][operator]": "gte", + "query[filters][1][value]": "2023-01-01", + "query[sort][0][field]": "name", + "query[sort][0][direction]": "asc", + "query[sort][1][field]": "created", + "query[sort][1][direction]": "desc", + }); + }); + }); + + describe("Error cases and malformed input", () => { + it("should handle circular references gracefully", () => { + const obj: any = { name: "test" }; + obj.self = obj; + + // This will throw a RangeError due to stack overflow - this is expected behavior + expect(() => encodeAsFormParameter(obj)).toThrow("Maximum call stack size exceeded"); + }); + + it("should handle very deeply nested objects", () => { + let deepObj: any = { value: "deep" }; + for (let i = 0; i < 100; i++) { + deepObj = { level: deepObj }; + } + + expect(() => encodeAsFormParameter(deepObj)).not.toThrow(); + const result = encodeAsFormParameter(deepObj); + expect(Object.keys(result).length).toBeGreaterThan(0); + }); + + it("should handle empty string splitting edge case", () => { + // Test what happens when qs returns an empty string + const result = encodeAsFormParameter({}); + expect(result).toEqual({}); + }); + }); +}); diff --git a/tests/unit/form-data-utils/formDataWrapper.browser.test.ts b/tests/unit/form-data-utils/formDataWrapper.browser.test.ts new file mode 100644 index 00000000..f7667618 --- /dev/null +++ b/tests/unit/form-data-utils/formDataWrapper.browser.test.ts @@ -0,0 +1,378 @@ +import { FormDataWrapper, newFormData } from "../../../src/core/form-data-utils/FormDataWrapper"; + +type FormDataRequest = ReturnType["getRequest"]>; + +async function getFormDataInfo(formRequest: FormDataRequest): Promise<{ + hasFile: boolean; + filename?: string; + contentType?: string; + serialized: string; +}> { + const request = new Request("http://localhost", { + ...formRequest, + method: "POST", + }); + const buffer = await request.arrayBuffer(); + const serialized = new TextDecoder().decode(buffer); + + const filenameMatch = serialized.match(/filename="([^"]+)"/); + const filename = filenameMatch ? filenameMatch[1] : undefined; + + const contentTypeMatch = serialized.match(/Content-Type: ([^\r\n]+)/); + const contentType = contentTypeMatch ? contentTypeMatch[1] : undefined; + + return { + hasFile: !!filename, + filename, + contentType, + serialized, + }; +} + +describe("FormDataWrapper - Browser Environment", () => { + let formData: FormDataWrapper; + + beforeEach(async () => { + formData = new FormDataWrapper(); + await formData.setup(); + }); + + describe("Web ReadableStream", () => { + it("serializes Web ReadableStream with filename", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("web stream content")); + controller.close(); + }, + }); + + await formData.appendFile("file", stream, "webstream.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="webstream.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("webstream.txt"); + }); + + it("handles empty Web ReadableStream", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.close(); + }, + }); + + await formData.appendFile("file", stream, "empty.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="empty.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("empty.txt"); + }); + }); + + describe("Browser-specific types", () => { + it("serializes Blob with specified filename and content type", async () => { + const blob = new Blob(["file content"], { type: "text/plain" }); + await formData.appendFile("file", blob, "testfile.txt"); + + const { serialized, hasFile, contentType, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="testfile.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("testfile.txt"); + expect(contentType).toBe("text/plain"); + }); + + it("serializes File and preserves filename", async () => { + const file = new File(["file content"], "testfile.txt", { type: "text/plain" }); + await formData.appendFile("file", file); + + const { serialized, hasFile, contentType, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="testfile.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("testfile.txt"); + expect(contentType).toBe("text/plain"); + }); + + it("allows filename override for File objects", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + await formData.appendFile("file", file, "override.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="override.txt"'); + expect(serialized).not.toContain('filename="original.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("override.txt"); + }); + }); + + describe("Binary data types", () => { + it("serializes ArrayBuffer with filename", async () => { + const arrayBuffer = new ArrayBuffer(8); + new Uint8Array(arrayBuffer).set([1, 2, 3, 4, 5, 6, 7, 8]); + + await formData.appendFile("file", arrayBuffer, "binary.bin"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="binary.bin"'); + expect(hasFile).toBe(true); + expect(filename).toBe("binary.bin"); + }); + + it("serializes Uint8Array with filename", async () => { + const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" + await formData.appendFile("file", uint8Array, "binary.bin"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="binary.bin"'); + expect(hasFile).toBe(true); + expect(filename).toBe("binary.bin"); + }); + + it("serializes other typed arrays", async () => { + const int16Array = new Int16Array([1000, 2000, 3000]); + await formData.appendFile("file", int16Array, "numbers.bin"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="numbers.bin"'); + expect(hasFile).toBe(true); + expect(filename).toBe("numbers.bin"); + }); + }); + + describe("Text and primitive types", () => { + it("serializes string as regular form field", async () => { + formData.append("text", "test string"); + + const { serialized, hasFile } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="text"'); + expect(serialized).not.toContain("filename="); + expect(serialized).toContain("test string"); + expect(hasFile).toBe(false); + }); + + it("serializes string as file with filename", async () => { + await formData.appendFile("file", "test content", "text.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="text.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("text.txt"); + }); + + it("serializes numbers and booleans as strings", async () => { + formData.append("number", 12345); + formData.append("flag", true); + + const { serialized } = await getFormDataInfo(formData.getRequest()); + expect(serialized).toContain("12345"); + expect(serialized).toContain("true"); + }); + }); + + describe("Object and JSON handling", () => { + it("serializes objects as JSON with filename", async () => { + const obj = { test: "value", nested: { key: "data" } }; + await formData.appendFile("data", obj, "data.json"); + + const { serialized, hasFile, contentType, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="data"'); + expect(serialized).toContain('filename="data.json"'); + expect(serialized).toContain("Content-Type: application/json"); + expect(hasFile).toBe(true); + expect(filename).toBe("data.json"); + expect(contentType).toBe("application/json"); + }); + + it("serializes arrays as JSON", async () => { + const arr = [1, 2, 3, "test"]; + await formData.appendFile("array", arr, "array.json"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="array"'); + expect(serialized).toContain('filename="array.json"'); + expect(hasFile).toBe(true); + expect(filename).toBe("array.json"); + }); + + it("handles null and undefined values", async () => { + formData.append("nullValue", null); + formData.append("undefinedValue", undefined); + + const { serialized } = await getFormDataInfo(formData.getRequest()); + expect(serialized).toContain("null"); + expect(serialized).toContain("undefined"); + }); + }); + + describe("Filename extraction from objects", () => { + it("extracts filename from object with name property", async () => { + const namedValue = { name: "custom-name.txt", data: "content" }; + await formData.appendFile("file", namedValue); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="custom-name.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("custom-name.txt"); + }); + + it("extracts filename from object with path property", async () => { + const pathedValue = { path: "/some/path/file.txt", content: "data" }; + await formData.appendFile("file", pathedValue); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="file.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("file.txt"); + }); + + it("prioritizes explicit filename over object properties", async () => { + const namedValue = { name: "original.txt", data: "content" }; + await formData.appendFile("file", namedValue, "override.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file"'); + expect(serialized).toContain('filename="override.txt"'); + expect(serialized).not.toContain('filename="original.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("override.txt"); + }); + }); + + describe("Edge cases and error handling", () => { + it("handles empty filename gracefully", async () => { + await formData.appendFile("file", "content", ""); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('Content-Disposition: form-data; name="file"'); + expect(serialized).toContain('filename="blob"'); // Default fallback + expect(hasFile).toBe(true); + expect(filename).toBe("blob"); + }); + + it("handles large strings", async () => { + const largeString = "x".repeat(1000); + await formData.appendFile("large", largeString, "large.txt"); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="large"'); + expect(serialized).toContain('filename="large.txt"'); + expect(hasFile).toBe(true); + expect(filename).toBe("large.txt"); + }); + + it("handles unicode content and filenames", async () => { + const unicodeContent = "Hello 世界 🌍 Emoji 🚀"; + const unicodeFilename = "файл-тест-🌟.txt"; + + await formData.appendFile("unicode", unicodeContent, unicodeFilename); + + const { serialized, hasFile, filename } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="unicode"'); + expect(serialized).toContain(`filename="${unicodeFilename}"`); + expect(hasFile).toBe(true); + expect(filename).toBe(unicodeFilename); + }); + + it("handles multiple files in single form", async () => { + await formData.appendFile("file1", "content1", "file1.txt"); + await formData.appendFile("file2", "content2", "file2.txt"); + formData.append("text", "regular field"); + + const { serialized } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toContain('name="file1"'); + expect(serialized).toContain('filename="file1.txt"'); + + expect(serialized).toContain('name="file2"'); + expect(serialized).toContain('filename="file2.txt"'); + + expect(serialized).toContain('name="text"'); + expect(serialized).not.toContain('filename="text"'); + expect(serialized).toContain("regular field"); + }); + }); + + describe("Request structure", () => { + it("returns correct request structure", async () => { + await formData.appendFile("file", "content", "test.txt"); + + const request = formData.getRequest(); + + expect(request).toHaveProperty("body"); + expect(request).toHaveProperty("headers"); + expect(request).toHaveProperty("duplex"); + expect(request.body).toBeInstanceOf(FormData); + expect(request.headers).toEqual({}); + expect(request.duplex).toBe("half"); + }); + + it("generates proper multipart boundary structure", async () => { + await formData.appendFile("file", "test content", "test.txt"); + formData.append("field", "value"); + + const { serialized } = await getFormDataInfo(formData.getRequest()); + + expect(serialized).toMatch(/------formdata-undici-\w+|------WebKitFormBoundary\w+/); + expect(serialized).toContain("Content-Disposition: form-data;"); + expect(serialized).toMatch(/------formdata-undici-\w+--|------WebKitFormBoundary\w+--/); + }); + }); + + describe("Factory function", () => { + it("returns FormDataWrapper instance", async () => { + const formData = await newFormData(); + expect(formData).toBeInstanceOf(FormDataWrapper); + }); + + it("creates independent instances", async () => { + const formData1 = await newFormData(); + const formData2 = await newFormData(); + + await formData1.setup(); + await formData2.setup(); + + formData1.append("test1", "value1"); + formData2.append("test2", "value2"); + + const request1 = formData1.getRequest() as { body: FormData }; + const request2 = formData2.getRequest() as { body: FormData }; + + const entries1 = Array.from(request1.body.entries()); + const entries2 = Array.from(request2.body.entries()); + + expect(entries1).toHaveLength(1); + expect(entries2).toHaveLength(1); + expect(entries1[0][0]).toBe("test1"); + expect(entries2[0][0]).toBe("test2"); + }); + }); +}); diff --git a/tests/unit/form-data-utils/formDataWrapper.test.ts b/tests/unit/form-data-utils/formDataWrapper.test.ts new file mode 100644 index 00000000..0ec0bcae --- /dev/null +++ b/tests/unit/form-data-utils/formDataWrapper.test.ts @@ -0,0 +1,360 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { Readable } from "stream"; +import { FormDataWrapper, newFormData } from "../../../src/core/form-data-utils/FormDataWrapper"; +import { File, Blob } from "buffer"; + +// Helper function to serialize FormData to string for inspection +async function serializeFormData(formData: FormData): Promise { + const request = new Request("http://localhost", { + method: "POST", + body: formData, + }); + + const buffer = await request.arrayBuffer(); + return new TextDecoder().decode(buffer); +} + +describe("FormDataWrapper", () => { + let formData: FormDataWrapper; + + beforeEach(async () => { + formData = new FormDataWrapper(); + await formData.setup(); + }); + + describe("Stream handling", () => { + it("serializes Node.js Readable stream with filename", async () => { + const stream = Readable.from(["file content"]); + await formData.appendFile("file", stream, "testfile.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + + expect(serialized).toContain('Content-Disposition: form-data; name="file"'); + expect(serialized).toContain('filename="testfile.txt"'); + expect(serialized).toContain("file content"); + }); + + it("auto-detects filename from stream path property", async () => { + const stream = Readable.from(["file content"]); + (stream as { path?: string }).path = "/test/path/testfile.txt"; + + await formData.appendFile("file", stream); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="testfile.txt"'); + }); + + it("handles Windows-style paths", async () => { + const stream = Readable.from(["file content"]); + (stream as { path?: string }).path = "C:\\test\\path\\testfile.txt"; + + await formData.appendFile("file", stream); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="testfile.txt"'); + }); + + it("handles empty streams", async () => { + const stream = Readable.from([]); + await formData.appendFile("file", stream, "empty.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="empty.txt"'); + expect(serialized).toMatch(/------formdata-undici-\w+|------WebKitFormBoundary\w+/); + }); + + it("serializes Web ReadableStream with filename", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("web stream content")); + controller.close(); + }, + }); + + await formData.appendFile("file", stream, "webstream.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="webstream.txt"'); + expect(serialized).toContain("web stream content"); + }); + + it("handles empty Web ReadableStream", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.close(); + }, + }); + + await formData.appendFile("file", stream, "empty.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="empty.txt"'); + expect(serialized).toMatch(/------formdata-undici-\w+|------WebKitFormBoundary\w+/); + }); + }); + + describe("Blob and File types", () => { + it("serializes Blob with specified filename", async () => { + const blob = new Blob(["file content"], { type: "text/plain" }); + await formData.appendFile("file", blob, "testfile.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="testfile.txt"'); + expect(serialized).toContain("Content-Type: text/plain"); + expect(serialized).toContain("file content"); + }); + + it("uses default filename for Blob without explicit filename", async () => { + const blob = new Blob(["file content"], { type: "text/plain" }); + await formData.appendFile("file", blob); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="blob"'); + }); + + it("preserves File object filename", async () => { + if (typeof File !== "undefined") { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + await formData.appendFile("file", file); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="original.txt"'); + expect(serialized).toContain("file content"); + } + }); + + it("allows filename override for File objects", async () => { + if (typeof File !== "undefined") { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + await formData.appendFile("file", file, "override.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="override.txt"'); + expect(serialized).not.toContain('filename="original.txt"'); + } + }); + }); + + describe("Binary data types", () => { + it("serializes ArrayBuffer with filename", async () => { + const arrayBuffer = new ArrayBuffer(8); + new Uint8Array(arrayBuffer).set([1, 2, 3, 4, 5, 6, 7, 8]); + + await formData.appendFile("file", arrayBuffer, "binary.bin"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="binary.bin"'); + expect(serialized).toMatch(/------formdata-undici-\w+|------WebKitFormBoundary\w+/); + }); + + it("serializes Uint8Array with filename", async () => { + const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" + await formData.appendFile("file", uint8Array, "binary.bin"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="binary.bin"'); + expect(serialized).toContain("Hello"); + }); + + it("serializes other typed arrays", async () => { + const int16Array = new Int16Array([1000, 2000, 3000]); + await formData.appendFile("file", int16Array, "numbers.bin"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="numbers.bin"'); + }); + + it("serializes Buffer data with filename", async () => { + if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function") { + const buffer = Buffer.from("test content"); + await formData.appendFile("file", buffer, "test.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="test.txt"'); + expect(serialized).toContain("test content"); + } + }); + }); + + describe("Text and primitive types", () => { + it("serializes string as regular form field", async () => { + formData.append("text", "test string"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('name="text"'); + expect(serialized).not.toContain("filename="); + expect(serialized).toContain("test string"); + }); + + it("serializes string as file with filename", async () => { + await formData.appendFile("file", "test content", "text.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="text.txt"'); + expect(serialized).toContain("test content"); + }); + + it("serializes numbers and booleans as strings", async () => { + formData.append("number", 12345); + formData.append("flag", true); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain("12345"); + expect(serialized).toContain("true"); + }); + }); + + describe("Object and JSON handling", () => { + it("serializes objects as JSON with filename", async () => { + const obj = { test: "value", nested: { key: "data" } }; + await formData.appendFile("data", obj, "data.json"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="data.json"'); + expect(serialized).toContain("Content-Type: application/json"); + expect(serialized).toContain(JSON.stringify(obj)); + }); + + it("serializes arrays as JSON", async () => { + const arr = [1, 2, 3, "test"]; + await formData.appendFile("array", arr, "array.json"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="array.json"'); + expect(serialized).toContain(JSON.stringify(arr)); + }); + + it("handles null and undefined values", async () => { + formData.append("nullValue", null); + formData.append("undefinedValue", undefined); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain("null"); + expect(serialized).toContain("undefined"); + }); + }); + + describe("Filename extraction from objects", () => { + it("extracts filename from object with name property", async () => { + const namedValue = { name: "custom-name.txt", data: "content" }; + await formData.appendFile("file", namedValue); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="custom-name.txt"'); + expect(serialized).toContain(JSON.stringify(namedValue)); + }); + + it("extracts filename from object with path property", async () => { + const pathedValue = { path: "/some/path/file.txt", content: "data" }; + await formData.appendFile("file", pathedValue); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="file.txt"'); + }); + + it("prioritizes explicit filename over object properties", async () => { + const namedValue = { name: "original.txt", data: "content" }; + await formData.appendFile("file", namedValue, "override.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="override.txt"'); + expect(serialized).not.toContain('filename="original.txt"'); + }); + }); + + describe("Edge cases and error handling", () => { + it("handles empty filename gracefully", async () => { + await formData.appendFile("file", "content", ""); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="blob"'); // Default fallback + }); + + it("handles large strings", async () => { + const largeString = "x".repeat(1000); + await formData.appendFile("large", largeString, "large.txt"); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="large.txt"'); + }); + + it("handles unicode content and filenames", async () => { + const unicodeContent = "Hello 世界 🌍 Emoji 🚀"; + const unicodeFilename = "файл-тест-🌟.txt"; + + await formData.appendFile("unicode", unicodeContent, unicodeFilename); + + const serialized = await serializeFormData(formData.getRequest().body); + expect(serialized).toContain('filename="' + unicodeFilename + '"'); + expect(serialized).toContain(unicodeContent); + }); + + it("handles multiple files in single form", async () => { + await formData.appendFile("file1", "content1", "file1.txt"); + await formData.appendFile("file2", "content2", "file2.txt"); + formData.append("text", "regular field"); + + const serialized = await serializeFormData(formData.getRequest().body); + + expect(serialized).toContain('filename="file1.txt"'); + expect(serialized).toContain('filename="file2.txt"'); + expect(serialized).toContain('name="text"'); + expect(serialized).not.toContain('filename="text"'); + }); + }); + + describe("Request structure", () => { + it("returns correct request structure", async () => { + await formData.appendFile("file", "content", "test.txt"); + + const request = formData.getRequest(); + + expect(request).toHaveProperty("body"); + expect(request).toHaveProperty("headers"); + expect(request).toHaveProperty("duplex"); + expect(request.body).toBeInstanceOf(FormData); + expect(request.headers).toEqual({}); + expect(request.duplex).toBe("half"); + }); + + it("generates proper multipart boundary structure", async () => { + await formData.appendFile("file", "test content", "test.txt"); + formData.append("field", "value"); + + const serialized = await serializeFormData(formData.getRequest().body); + + expect(serialized).toMatch(/------formdata-undici-\w+|------WebKitFormBoundary\w+/); + expect(serialized).toContain("Content-Disposition: form-data;"); + expect(serialized).toMatch(/------formdata-undici-\w+--|------WebKitFormBoundary\w+--/); + }); + }); + + describe("Factory function", () => { + it("returns FormDataWrapper instance", async () => { + const formData = await newFormData(); + expect(formData).toBeInstanceOf(FormDataWrapper); + }); + + it("creates independent instances", async () => { + const formData1 = await newFormData(); + const formData2 = await newFormData(); + + await formData1.setup(); + await formData2.setup(); + + formData1.append("test1", "value1"); + formData2.append("test2", "value2"); + + const request1 = formData1.getRequest() as { body: FormData }; + const request2 = formData2.getRequest() as { body: FormData }; + + const entries1 = Array.from(request1.body.entries()); + const entries2 = Array.from(request2.body.entries()); + + expect(entries1).toHaveLength(1); + expect(entries2).toHaveLength(1); + expect(entries1[0][0]).toBe("test1"); + expect(entries2[0][0]).toBe("test2"); + }); + }); +}); diff --git a/tests/unit/url/join.test.ts b/tests/unit/url/join.test.ts new file mode 100644 index 00000000..984cfe67 --- /dev/null +++ b/tests/unit/url/join.test.ts @@ -0,0 +1,120 @@ +import { join } from "../../../src/core/url/index"; + +describe("join", () => { + describe("basic functionality", () => { + it("should return empty string for empty base", () => { + expect(join("")).toBe(""); + expect(join("", "path")).toBe(""); + }); + + it("should handle single segment", () => { + expect(join("base", "segment")).toBe("base/segment"); + expect(join("base/", "segment")).toBe("base/segment"); + expect(join("base", "/segment")).toBe("base/segment"); + expect(join("base/", "/segment")).toBe("base/segment"); + }); + + it("should handle multiple segments", () => { + expect(join("base", "path1", "path2", "path3")).toBe("base/path1/path2/path3"); + expect(join("base/", "/path1/", "/path2/", "/path3/")).toBe("base/path1/path2/path3/"); + }); + }); + + describe("URL handling", () => { + it("should handle absolute URLs", () => { + expect(join("https://example.com", "api", "v1")).toBe("https://example.com/api/v1"); + expect(join("https://example.com/", "/api/", "/v1/")).toBe("https://example.com/api/v1/"); + expect(join("https://example.com/base", "api", "v1")).toBe("https://example.com/base/api/v1"); + }); + + it("should preserve URL query parameters and fragments", () => { + expect(join("https://example.com?query=1", "api")).toBe("https://example.com/api?query=1"); + expect(join("https://example.com#fragment", "api")).toBe("https://example.com/api#fragment"); + expect(join("https://example.com?query=1#fragment", "api")).toBe( + "https://example.com/api?query=1#fragment", + ); + }); + + it("should handle different protocols", () => { + expect(join("http://example.com", "api")).toBe("http://example.com/api"); + expect(join("ftp://example.com", "files")).toBe("ftp://example.com/files"); + expect(join("ws://example.com", "socket")).toBe("ws://example.com/socket"); + }); + + it("should fallback to path joining for malformed URLs", () => { + expect(join("not-a-url://", "path")).toBe("not-a-url:///path"); + }); + }); + + describe("edge cases", () => { + it("should handle empty segments", () => { + expect(join("base", "", "path")).toBe("base/path"); + expect(join("base", null as any, "path")).toBe("base/path"); + expect(join("base", undefined as any, "path")).toBe("base/path"); + }); + + it("should handle segments with only slashes", () => { + expect(join("base", "/", "path")).toBe("base/path"); + expect(join("base", "//", "path")).toBe("base/path"); + }); + + it("should handle base paths with trailing slashes", () => { + expect(join("base/", "path")).toBe("base/path"); + }); + + it("should handle complex nested paths", () => { + expect(join("api/v1/", "/users/", "/123/", "/profile")).toBe("api/v1/users/123/profile"); + }); + }); + + describe("real-world scenarios", () => { + it("should handle API endpoint construction", () => { + const baseUrl = "https://api.example.com/v1"; + expect(join(baseUrl, "users", "123", "posts")).toBe("https://api.example.com/v1/users/123/posts"); + }); + + it("should handle file path construction", () => { + expect(join("/var/www", "html", "assets", "images")).toBe("/var/www/html/assets/images"); + }); + + it("should handle relative path construction", () => { + expect(join("../parent", "child", "grandchild")).toBe("../parent/child/grandchild"); + }); + + it("should handle Windows-style paths", () => { + expect(join("C:\\Users", "Documents", "file.txt")).toBe("C:\\Users/Documents/file.txt"); + }); + }); + + describe("performance scenarios", () => { + it("should handle many segments efficiently", () => { + const segments = Array(100).fill("segment"); + const result = join("base", ...segments); + expect(result).toBe("base/" + segments.join("/")); + }); + + it("should handle long URLs", () => { + const longPath = "a".repeat(1000); + expect(join("https://example.com", longPath)).toBe(`https://example.com/${longPath}`); + }); + }); + + describe("trailing slash preservation", () => { + it("should preserve trailing slash on final result when base has trailing slash and no segments", () => { + expect(join("https://api.example.com/")).toBe("https://api.example.com/"); + expect(join("https://api.example.com/v1/")).toBe("https://api.example.com/v1/"); + }); + + it("should preserve trailing slash when last segment has trailing slash", () => { + expect(join("https://api.example.com", "users/")).toBe("https://api.example.com/users/"); + expect(join("api/v1", "users/")).toBe("api/v1/users/"); + }); + + it("should preserve trailing slash with multiple segments where last has trailing slash", () => { + expect(join("https://api.example.com", "v1", "collections/")).toBe( + "https://api.example.com/v1/collections/", + ); + expect(join("base", "path1", "path2/")).toBe("base/path1/path2/"); + }); + }); +}); diff --git a/tests/unit/url/qs.test.ts b/tests/unit/url/qs.test.ts new file mode 100644 index 00000000..80e7e044 --- /dev/null +++ b/tests/unit/url/qs.test.ts @@ -0,0 +1,187 @@ +import { toQueryString } from "../../../src/core/url/index"; + +describe("Test qs toQueryString", () => { + describe("Basic functionality", () => { + it("should return empty string for null/undefined", () => { + expect(toQueryString(null)).toBe(""); + expect(toQueryString(undefined)).toBe(""); + }); + + it("should return empty string for primitive values", () => { + expect(toQueryString("hello")).toBe(""); + expect(toQueryString(42)).toBe(""); + expect(toQueryString(true)).toBe(""); + expect(toQueryString(false)).toBe(""); + }); + + it("should handle empty objects", () => { + expect(toQueryString({})).toBe(""); + }); + + it("should handle simple key-value pairs", () => { + const obj = { name: "John", age: 30 }; + expect(toQueryString(obj)).toBe("name=John&age=30"); + }); + }); + + describe("Array handling", () => { + it("should handle arrays with indices format (default)", () => { + const obj = { items: ["a", "b", "c"] }; + expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c"); + }); + + it("should handle arrays with repeat format", () => { + const obj = { items: ["a", "b", "c"] }; + expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe("items=a&items=b&items=c"); + }); + + it("should handle empty arrays", () => { + const obj = { items: [] }; + expect(toQueryString(obj)).toBe(""); + }); + + it("should handle arrays with mixed types", () => { + const obj = { mixed: ["string", 42, true, false] }; + expect(toQueryString(obj)).toBe("mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false"); + }); + + it("should handle arrays with objects", () => { + const obj = { users: [{ name: "John" }, { name: "Jane" }] }; + expect(toQueryString(obj)).toBe("users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane"); + }); + + it("should handle arrays with objects in repeat format", () => { + const obj = { users: [{ name: "John" }, { name: "Jane" }] }; + expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe("users%5Bname%5D=John&users%5Bname%5D=Jane"); + }); + }); + + describe("Nested objects", () => { + it("should handle nested objects", () => { + const obj = { user: { name: "John", age: 30 } }; + expect(toQueryString(obj)).toBe("user%5Bname%5D=John&user%5Bage%5D=30"); + }); + + it("should handle deeply nested objects", () => { + const obj = { user: { profile: { name: "John", settings: { theme: "dark" } } } }; + expect(toQueryString(obj)).toBe( + "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + ); + }); + + it("should handle empty nested objects", () => { + const obj = { user: {} }; + expect(toQueryString(obj)).toBe(""); + }); + }); + + describe("Encoding", () => { + it("should encode by default", () => { + const obj = { name: "John Doe", email: "john@example.com" }; + expect(toQueryString(obj)).toBe("name=John%20Doe&email=john%40example.com"); + }); + + it("should not encode when encode is false", () => { + const obj = { name: "John Doe", email: "john@example.com" }; + expect(toQueryString(obj, { encode: false })).toBe("name=John Doe&email=john@example.com"); + }); + + it("should encode special characters in keys", () => { + const obj = { "user name": "John", "email[primary]": "john@example.com" }; + expect(toQueryString(obj)).toBe("user%20name=John&email%5Bprimary%5D=john%40example.com"); + }); + + it("should not encode special characters in keys when encode is false", () => { + const obj = { "user name": "John", "email[primary]": "john@example.com" }; + expect(toQueryString(obj, { encode: false })).toBe("user name=John&email[primary]=john@example.com"); + }); + }); + + describe("Mixed scenarios", () => { + it("should handle complex nested structures", () => { + const obj = { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }; + expect(toQueryString(obj)).toBe( + "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + ); + }); + + it("should handle complex nested structures with repeat format", () => { + const obj = { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, + }, + sort: { field: "name", direction: "asc" }, + }; + expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe( + "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + ); + }); + + it("should handle arrays with null/undefined values", () => { + const obj = { items: ["a", null, "c", undefined, "e"] }; + expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e"); + }); + + it("should handle objects with null/undefined values", () => { + const obj = { name: "John", age: null, email: undefined, active: true }; + expect(toQueryString(obj)).toBe("name=John&age=&active=true"); + }); + }); + + describe("Edge cases", () => { + it("should handle numeric keys", () => { + const obj = { "0": "zero", "1": "one" }; + expect(toQueryString(obj)).toBe("0=zero&1=one"); + }); + + it("should handle boolean values in objects", () => { + const obj = { enabled: true, disabled: false }; + expect(toQueryString(obj)).toBe("enabled=true&disabled=false"); + }); + + it("should handle empty strings", () => { + const obj = { name: "", description: "test" }; + expect(toQueryString(obj)).toBe("name=&description=test"); + }); + + it("should handle zero values", () => { + const obj = { count: 0, price: 0.0 }; + expect(toQueryString(obj)).toBe("count=0&price=0"); + }); + + it("should handle arrays with empty strings", () => { + const obj = { items: ["a", "", "c"] }; + expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c"); + }); + }); + + describe("Options combinations", () => { + it("should respect both arrayFormat and encode options", () => { + const obj = { items: ["a & b", "c & d"] }; + expect(toQueryString(obj, { arrayFormat: "repeat", encode: false })).toBe("items=a & b&items=c & d"); + }); + + it("should use default options when none provided", () => { + const obj = { items: ["a", "b"] }; + expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=b"); + }); + + it("should merge provided options with defaults", () => { + const obj = { items: ["a", "b"], name: "John Doe" }; + expect(toQueryString(obj, { encode: false })).toBe("items[0]=a&items[1]=b&name=John Doe"); + }); + }); +}); diff --git a/tests/upload.js b/tests/upload.js deleted file mode 100644 index 935c6f18..00000000 --- a/tests/upload.js +++ /dev/null @@ -1,599 +0,0 @@ -import chai from "chai"; -import sinon from "sinon"; -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import nock from "nock"; -import fs from "fs"; -import path from "path"; - -function checkFormData({requestBody, boundary, fieldName, fieldValue}) { - return expect(requestBody).include(`${boundary}\r\nContent-Disposition: form-data; name="${fieldName}"\r\n\r\n${fieldValue}`) -} - -const uploadSuccessResponseObj = { - "fileId": "598821f949c0a938d57563bd", - "name": "file1.jpg", - "url": "https://ik.imagekit.io/your_imagekit_id/images/products/file1.jpg", - "thumbnailUrl": "https://ik.imagekit.io/your_imagekit_id/tr:n-media_library_thumbnail/images/products/file1.jpg", - "height": 300, - "width": 200, - "size": 83622, - "filePath": "/images/products/file1.jpg", - "tags": ["t-shirt", "round-neck", "sale2019"], - "isPrivateFile": false, - "customCoordinates": null, - "fileType": "image", - "AITags":[{"name":"Face","confidence":99.95,"source":"aws-auto-tagging"}], - "extensionStatus":{"aws-auto-tagging":"success"} -}; - -describe("File upload custom endpoint", function () { - var imagekit = new ImageKit({ - ...initializationParams, - uploadEndpoint: "https://custom-env.imagekit.io/api/v1/files/upload" - }); - - it('Upload endpoint test case', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://custom-env.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); - done(); - },10); - }); -}); - -describe("File upload", function () { - var imagekit = new ImageKit(initializationParams); - - it('Invalid upload params', function () { - var callback = sinon.spy(); - - imagekit.upload(null, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing data for upload" }, null); - }); - - it('Missing fileName', function () { - const fileOptions = { - file: "https://ik.imagekit.io/remote-url.jpg" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing fileName parameter for upload" }, null); - }); - - it('Missing file', function () { - const fileOptions = { - fileName: "test_file_name", - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); - }); - - it('Full request', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - tags: ["tag1","tag2"], // array handling - isPrivateFile: true, // Boolean handling - useUniqueFileName: "false", // As string - responseFields: ["tags", "metadata"], - extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } - ], - webhookUrl: "https://your-domain/?appId=some-id", - overwriteFile: true, - overwriteAITags: false, - overwriteTags: true, - overwriteCustomMetadata: false, - customMetadata: { - brand: "Nike", - color: "red" - }, - }; - - var callback = sinon.spy(); - var jsonStringifiedExtensions = JSON.stringify(fileOptions.extensions); - const customMetadata = JSON.stringify(fileOptions.customMetadata); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"tags",fieldValue:"tag1,tag2"}); - checkFormData({requestBody,boundary,fieldName:"isPrivateFile",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"useUniqueFileName",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"responseFields",fieldValue:"tags,metadata"}); - checkFormData({requestBody,boundary,fieldName:"extensions",fieldValue:jsonStringifiedExtensions}); - checkFormData({requestBody,boundary,fieldName:"webhookUrl",fieldValue:"https://your-domain/?appId=some-id"}); - checkFormData({requestBody,boundary,fieldName:"overwriteFile",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"overwriteAITags",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"overwriteTags",fieldValue:"true"}); - checkFormData({requestBody,boundary,fieldName:"overwriteCustomMetadata",fieldValue:"false"}); - checkFormData({requestBody,boundary,fieldName:"customMetadata",fieldValue:customMetadata}); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Buffer file smaller than 10MB', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: fs.readFileSync(path.join(__dirname,"./data/test_image.jpg")) - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - expect(requestBody.length).equal(399064); - done() - }) - - imagekit.upload(fileOptions); - }); - - it('Buffer file larger than 10MB', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: Buffer.alloc(15000000), // static buffer of 15 MB size - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - expect(requestBody.length).equal(15000347); - }) - - imagekit.upload(fileOptions, function (err, result) { - expect(err).to.equal(null) - done(); - }); - }); - - it('Missing useUniqueFileName', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - isPrivateFile: true - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"isPrivateFile",fieldValue:"true"}); - expect(requestBody).to.not.include("useUniqueFileName"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Missing isPrivateFile and useUniqueFileName', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - tags: "tag1,tag2" // as string - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - checkFormData({requestBody,boundary,fieldName:"tags",fieldValue:"tag1,tag2"}); - expect(requestBody).to.not.include("useUniqueFileName"); - expect(requestBody).to.not.include("isPrivateFile"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Bare minimum request', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=",""); - checkFormData({requestBody,boundary,fieldName:"fileName",fieldValue:fileOptions.fileName}); - checkFormData({requestBody,boundary,fieldName:"file",fieldValue:fileOptions.file}); - expect(requestBody).to.not.include("tags"); - expect(requestBody).to.not.include("useUniqueFileName"); - expect(requestBody).to.not.include("isPrivateFile"); - expect(requestBody).to.not.include("customCoordinates"); - expect(requestBody).to.not.include("responseFields"); - done() - }) - - imagekit.upload(fileOptions, callback); - }); - - it('Success callback', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); - done(); - },10); - }); - - it('Success using promise', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(200, uploadSuccessResponseObj) - - imagekit.upload(fileOptions) - .then((response, error) => { - expect(response).to.been.deep.equal(uploadSuccessResponseObj) - done(); - }); - }); - - it('Network error', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .replyWithError("Network error occured") - - imagekit.upload(fileOptions, function(err, response) { - expect(err.message).equal("Network error occured"); - done(); - }); - }); - - it('Server side error', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var callback = sinon.spy(); - - var error = { - help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(403, error) - - imagekit.upload(fileOptions, callback); - - setTimeout( () => { - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, error, null); - done(); - },10); - }); - - it('Server side error promise', function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content" - }; - - var error = { - help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." - }; - - const scope = nock('https://upload.imagekit.io/api') - .post('/v1/files/upload') - .basicAuth({ user: initializationParams.privateKey, pass: '' }) - .reply(403, error) - - imagekit.upload(fileOptions) - .then((response, error) => { - }) - .catch(error => { - expect(error).to.been.deep.equal(error) - done(); - }) - }); - - it("With pre and post transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { pre: "w-100", post: [{ type: "transformation", value: "h-100" }] }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With pre transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { pre: "w-100" }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With post transformation", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - transformation: { post: [{ type: "transformation", value: "h-100" }] }, - }; - - var callback = sinon.spy(); - const transformation = JSON.stringify(fileOptions.transformation); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "transformation", fieldValue: transformation }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("Should return error for an invalid transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: {}, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid transformation parameter. Please include at least pre, post, or both.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid pre transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { pre: "" }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid pre transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation of type abs", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: [{ type: "abs", value: "" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation of type transformation", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: [{ type: "transformation", value: "" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("Should return error for an invalid post transformation if it's not an array", async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: {} }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); - }); - - it("With checks option", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - checks: "'request.folder' : '/'", - }; - - var callback = sinon.spy(); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "checks", fieldValue: fileOptions.checks }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); - - it("With isPublished option", function (done) { - const fileOptions = { - fileName: "test_file_name", - file: "test_file_content", - isPublished: false - }; - - var callback = sinon.spy(); - - const scope = nock("https://upload.imagekit.io/api") - .post("/v1/files/upload") - .basicAuth({ user: initializationParams.privateKey, pass: "" }) - .reply(200, function (uri, requestBody) { - expect(this.req.headers["content-type"]).include("multipart/form-data; boundary=---------------------"); - var boundary = this.req.headers["content-type"].replace("multipart/form-data; boundary=", ""); - checkFormData({ requestBody, boundary, fieldName: "fileName", fieldValue: fileOptions.fileName }); - checkFormData({ requestBody, boundary, fieldName: "file", fieldValue: fileOptions.file }); - checkFormData({ requestBody, boundary, fieldName: "isPublished", fieldValue: fileOptions.isPublished }); - done(); - }); - - imagekit.upload(fileOptions, callback); - }); -}); diff --git a/tests/url-generation.js b/tests/url-generation.js deleted file mode 100644 index 689e234e..00000000 --- a/tests/url-generation.js +++ /dev/null @@ -1,446 +0,0 @@ -import chai from "chai"; -const pkg = require("../package.json"); -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../index"; -import { encodeStringIfRequired, getSignature } from "../libs/url/builder"; -var imagekit = new ImageKit(initializationParams); - -describe("URL generation", function () { - it('no path no src', function () { - const url = imagekit.url({}); - - expect(url).equal(""); - }); - - it('no transformation path', function () { - const url = imagekit.url({ - path: "/test_path.jpg" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); - }); - - it('no transformation src', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - }); - - it('Undefined parameters with path', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - transformation: undefined, - transformationPosition: undefined, - src: undefined, - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - }); - - it('Signed URL', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - signed: true - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?ik-s=e26ca157df99b30b2443d7cb6886fc396fb4c87b`); - }); - - it('Signed URL with expireSeconds', function () { - const url = imagekit.url({ - path: "/test_path_alt.jpg", - signed: true, - expireSeconds: 100 - }); - - expect(url).includes(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - expect(url).includes(`ik-s=`); - }); - - it("Signed URL with é in filename", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/test_é_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_%C3%A9_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with é in filename and path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/aéb/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with é in filename, path and transformation as path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekité,fs-50,l-end/aéb/test_é_path_alt.jpg"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit%C3%A9,fs-50,l-end/a%C3%A9b/test_%C3%A9_path_alt.jpg"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekité,fs-50,l-end" }], - transformationPosition: "path", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit%C3%A9,fs-50,l-end/a%C3%A9b/test_%C3%A9_path_alt.jpg?ik-s=${signature}` - ); - }); - - it("Signed URL with é in filename, path and transformation as query", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/aéb/test_é_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end"; - const encodedUrl = encodeStringIfRequired(testURL); - expect(encodedUrl).equal("https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end"); - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/aéb/test_é_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekité,fs-50,l-end" }], - transformationPosition: "query", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end&ik-s=${signature}` - ); - }); - - - it('should generate the correct url with path param', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); - }); - - it('should generate the correct url with path param with multiple leading slash', function () { - const url = imagekit.url({ - path: "///test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); - - }); - - it('should generate the correct url with path param with overidden urlEndpoint', function () { - const url = imagekit.url({ - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint_alt", - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/tr:h-300,w-400/test_path.jpg`); - - }); - - it('should generate the correct url with path param with transformationPosition as query', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with src param', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with transformationPosition as query', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); - }); - - it('should generate the correct url with query params properly merged', function () { - const url = imagekit.url({ - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", - queryParameters: { t2: "v2", t3: "v3" }, - transformation: [{ - "height": "300", - "width": "400" - }] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=h-300%2Cw-400`); - }) - - - it('should generate the correct chained transformation', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rt": "90" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400:rt-90/test_path.jpg`); - }); - - - it('should generate the correct chained transformation url with new undocumented tranforamtion parameter', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rndm_trnsf": "abcd" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400:rndm_trnsf-abcd/test_path.jpg`); - }); - - it('Overlay image', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end/test_path.jpg`); - }); - - it('Overlay image with slash in path', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end/test_path.jpg`); - }); - - it('Border', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - border: "20_FF0000" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,b-20_FF0000/test_path.jpg`); - }); - - it('e-sharpen - ', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - "e-sharpen": "-", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path.jpg`); - }); - - - it('transformation with defaultImage', function () { - const url = imagekit.url({ - path: "/test_path1.jpg", - transformation: [{ - defaultImage: "test_path.jpg", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); - }); - - it('skip transformation if it is undefined or null', function () { - const url = imagekit.url({ - path: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - quality: undefined, - effectContrast: null - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); - }); - - it("Signed URL with ' in filename", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/test_'_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_'_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with ' in filename and path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?ik-s=${signature}`); - }); - - it("Signed URL with ' in filename, path and transformation as path", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit',fs-50,l-end/a'b/test_'_path_alt.jpg"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekit',fs-50,l-end" }], - transformationPosition: "path", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Imagekit',fs-50,l-end/a'b/test_'_path_alt.jpg?ik-s=${signature}` - ); - }); - - it("Signed URL with ' in filename, path and transformation as query", function () { - const testURL = "https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?tr=l-text%2Ci-Imagekit%27%2Cfs-50%2Cl-end"; - const signature = getSignature({ - privateKey: "test_private_key", - url: testURL, - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - expiryTimestamp: "9999999999", - }); - const url = imagekit.url({ - path: "/a'b/test_'_path_alt.jpg", - signed: true, - transformation: [{ raw: "l-text,i-Imagekit',fs-50,l-end" }], - transformationPosition: "query", - }); - expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/a'b/test_'_path_alt.jpg?tr=l-text%2Ci-Imagekit%27%2Cfs-50%2Cl-end&ik-s=${signature}` - ); - }); - - - it('All combined', function () { - const url = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - height: 300, - width: 400, - aspectRatio: '4-3', - quality: 40, - crop: 'force', - cropMode: 'extract', - focus: 'left', - format: 'jpeg', - radius: 50, - bg: "A94D34", - border: "5-A94D34", - rotation: 90, - blur: 10, - named: "some_name", - progressive: true, - lossless: true, - trim: 5, - metadata: true, - colorProfile: true, - defaultImage: "/folder/file.jpg/", //trailing and leading slash case - dpr: 3, - effectSharpen: 10, - effectUSM: "2-2-0.8-0.024", - effectContrast: true, - effectGray: true, - original: true, - effectShadow: 'bl-15_st-40_x-10_y-N5', - effectGradient: 'from-red_to-white', - raw: "h-200,w-300,l-image,i-logo.png,l-end", - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast-true,e-grayscale-true,orig-true,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); - }); -}); - - diff --git a/tests/webhook-signature.js b/tests/webhook-signature.js deleted file mode 100644 index 4e3dd4a5..00000000 --- a/tests/webhook-signature.js +++ /dev/null @@ -1,145 +0,0 @@ -import ImageKit from "../index"; -import { expect } from "chai"; - -// Sample webhook data -const WEBHOOK_REQUEST_SAMPLE_SECRET = "whsec_xeO2UNkfKMQnfJf7Q/Qx+fYptL1wabXd"; -const WEBHOOK_REQUEST_SAMPLE_TIMESTAMP = new Date(1655788406333); -const WEBHOOK_REQUEST_SAMPLE_SIGNATURE_HEADER = - "t=1655788406333,v1=d30758f47fcb31e1fa0109d3b3e2a6c623e699aaf1461cba6bd462ef58ea4b31"; -const WEBHOOK_REQUEST_SAMPLE_RAW_BODY = - '{"type":"video.transformation.accepted","id":"58e6d24d-6098-4319-be8d-40c3cb0a402d","created_at":"2022-06-20T11:59:58.461Z","request":{"x_request_id":"fa98fa2e-d6cd-45b4-acf5-bc1d2bbb8ba9","url":"http://ik.imagekit.io/demo/sample-video.mp4?tr=f-webm,q-10","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0"},"data":{"asset":{"url":"http://ik.imagekit.io/demo/sample-video.mp4"},"transformation":{"type":"video-transformation","options":{"video_codec":"vp9","audio_codec":"opus","auto_rotate":true,"quality":10,"format":"webm"}}}}'; -const WEBHOOK_REQUEST_SAMPLE = Object.seal({ - secret: WEBHOOK_REQUEST_SAMPLE_SECRET, - timestamp: WEBHOOK_REQUEST_SAMPLE_TIMESTAMP, - signatureHeader: WEBHOOK_REQUEST_SAMPLE_SIGNATURE_HEADER, - rawBody: WEBHOOK_REQUEST_SAMPLE_RAW_BODY, - body: JSON.parse(WEBHOOK_REQUEST_SAMPLE_RAW_BODY), -}); - -describe("WebhookSignature", function () { - const verify = (new ImageKit(require("./data").initializationParams)).verifyWebhookEvent; - - context("Test Webhook.verify() - Positive cases", () => { - it("Verify with body as string", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const { timestamp, event } = verify( - webhookRequest.rawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - it("Verify with body as Buffer", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const { timestamp, event } = verify( - Buffer.from(webhookRequest.rawBody), - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - it("Verify with body as Uint8Array", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const rawBody = Uint8Array.from(Buffer.from(webhookRequest.rawBody)); - const { timestamp, event } = verify( - rawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect(timestamp).to.equal(webhookRequest.timestamp.getTime()); - expect(event).to.deep.equal(webhookRequest.body); - }); - }); - - context("Test WebhookSignature.verify() - Negative cases", () => { - it("Timestamp missing", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = - "v1=b6bc2aa82491c32f1cbef0eb52b7ffffff467ea65a03b5d4ccdcfb9e0941c946"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Timestamp missing"); - } - }); - it("Timestamp invalid", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = - "t=notANumber,v1=b6bc2aa82491c32f1cbef0eb52b7ffffff467ea65a03b5d4ccdcfb9e0941c946"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Timestamp invalid"); - } - }); - it("Signature missing", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = "t=1656326161409"; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Signature missing"); - } - }); - it("Incorrect signature - v1 manipulated", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const invalidSignature = `t=${webhookRequest.timestamp.getTime()},v1=d66b01d8f1e158d1af7646184716037510ac8ce0a1e70b726a1b698f954785b2`; - try { - verify(webhookRequest.rawBody, invalidSignature, webhookRequest.secret); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - incorrect request body", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const incorrectBody = { hello: "world" }; - const incorrectRawBody = JSON.stringify(incorrectBody); - try { - verify( - incorrectRawBody, - webhookRequest.signatureHeader, - webhookRequest.secret - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - timestamp manipulated", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - const incorrectSignature = webhookRequest.signatureHeader.replace( - `t=${webhookRequest.timestamp.getTime()}`, - `t=${webhookRequest.timestamp.getTime() + 1}` - ); // Correct timestamp replaced with incorrect timestamp - try { - verify( - webhookRequest.rawBody, - incorrectSignature, - webhookRequest.secret - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - it("Incorrect signature - different secret", () => { - const webhookRequest = WEBHOOK_REQUEST_SAMPLE; - try { - verify( - webhookRequest.rawBody, - webhookRequest.signatureHeader, - "A different secret" - ); - expect.fail("Expected exception"); - } catch (e) { - expect(e.message).to.equal("Incorrect signature"); - } - }); - }); -}); diff --git a/tests/wire/.gitkeep b/tests/wire/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/wire/accounts/origins.test.ts b/tests/wire/accounts/origins.test.ts new file mode 100644 index 00000000..54702c9e --- /dev/null +++ b/tests/wire/accounts/origins.test.ts @@ -0,0 +1,188 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Origins", () => { + test("list", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = [ + { + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { key: "value" }, + id: "id", + type: "S3", + }, + ]; + server + .mockEndpoint() + .get("/v1/accounts/origins") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.origins.list(); + expect(response).toEqual([ + { + type: "S3", + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { + key: "value", + }, + id: "id", + }, + ]); + }); + + test("create", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + type: "S3", + }; + const rawResponseBody = { + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { key: "value" }, + id: "id", + type: "S3", + }; + server + .mockEndpoint() + .post("/v1/accounts/origins") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.origins.create({ + type: "S3", + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }); + expect(response).toEqual({ + type: "S3", + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { + key: "value", + }, + id: "id", + }); + }); + + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { key: "value" }, + id: "id", + type: "S3", + }; + server + .mockEndpoint() + .get("/v1/accounts/origins/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.origins.get("id"); + expect(response).toEqual({ + type: "S3", + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { + key: "value", + }, + id: "id", + }); + }); + + test("update", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + type: "S3", + }; + const rawResponseBody = { + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { key: "value" }, + id: "id", + type: "S3", + }; + server + .mockEndpoint() + .put("/v1/accounts/origins/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.origins.update("id", { + type: "S3", + name: "US S3 Storage", + bucket: "product-images", + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + }); + expect(response).toEqual({ + type: "S3", + name: "US S3 Storage", + includeCanonicalHeader: false, + baseUrlForCanonicalHeader: "https://cdn.example.com", + bucket: "product-images", + prefix: { + key: "value", + }, + id: "id", + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/v1/accounts/origins/id").respondWith().statusCode(200).build(); + + const response = await client.accounts.origins.delete("id"); + expect(response).toEqual(undefined); + }); +}); diff --git a/tests/wire/accounts/urlEndpoints.test.ts b/tests/wire/accounts/urlEndpoints.test.ts new file mode 100644 index 00000000..9a7f8d9c --- /dev/null +++ b/tests/wire/accounts/urlEndpoints.test.ts @@ -0,0 +1,168 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("UrlEndpoints", () => { + test("list", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = [ + { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { preserveAssetDeliveryTypes: true, type: "CLOUDINARY" }, + id: "id", + }, + ]; + server + .mockEndpoint() + .get("/v1/accounts/url-endpoints") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.urlEndpoints.list(); + expect(response).toEqual([ + { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { + type: "CLOUDINARY", + preserveAssetDeliveryTypes: true, + }, + id: "id", + }, + ]); + }); + + test("create", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], + }; + const rawResponseBody = { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { preserveAssetDeliveryTypes: true, type: "CLOUDINARY" }, + id: "id", + }; + server + .mockEndpoint() + .post("/v1/accounts/url-endpoints") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.urlEndpoints.create({ + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], + }); + expect(response).toEqual({ + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { + type: "CLOUDINARY", + preserveAssetDeliveryTypes: true, + }, + id: "id", + }); + }); + + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { preserveAssetDeliveryTypes: true, type: "CLOUDINARY" }, + id: "id", + }; + server + .mockEndpoint() + .get("/v1/accounts/url-endpoints/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.urlEndpoints.get("id"); + expect(response).toEqual({ + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { + type: "CLOUDINARY", + preserveAssetDeliveryTypes: true, + }, + id: "id", + }); + }); + + test("update", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], + }; + const rawResponseBody = { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { preserveAssetDeliveryTypes: true, type: "CLOUDINARY" }, + id: "id", + }; + server + .mockEndpoint() + .put("/v1/accounts/url-endpoints/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.accounts.urlEndpoints.update("id", { + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1"], + }); + expect(response).toEqual({ + description: "My custom URL endpoint", + urlPrefix: "product-images", + origins: ["origin-id-1", "origin-id-2"], + urlRewriter: { + type: "CLOUDINARY", + preserveAssetDeliveryTypes: true, + }, + id: "id", + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/v1/accounts/url-endpoints/id").respondWith().statusCode(200).build(); + + const response = await client.accounts.urlEndpoints.delete("id"); + expect(response).toEqual(undefined); + }); +}); diff --git a/tests/wire/accounts/usage.test.ts b/tests/wire/accounts/usage.test.ts new file mode 100644 index 00000000..b4a79b11 --- /dev/null +++ b/tests/wire/accounts/usage.test.ts @@ -0,0 +1,34 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Usage", () => { + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + bandwidthBytes: 21991583578, + mediaLibraryStorageBytes: 1878758298, + videoProcessingUnitsCount: 0, + extensionUnitsCount: 0, + originalCacheStorageBytes: 0, + }; + server.mockEndpoint().get("/v1/accounts/usage").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.accounts.usage.get({ + startDate: "startDate", + endDate: "endDate", + }); + expect(response).toEqual({ + bandwidthBytes: 21991583578, + mediaLibraryStorageBytes: 1878758298, + videoProcessingUnitsCount: 0, + extensionUnitsCount: 0, + originalCacheStorageBytes: 0, + }); + }); +}); diff --git a/tests/wire/assets.test.ts b/tests/wire/assets.test.ts new file mode 100644 index 00000000..4947333e --- /dev/null +++ b/tests/wire/assets.test.ts @@ -0,0 +1,169 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../mock-server/MockServerPool"; +import { ImageKitClient } from "../../src/Client"; + +describe("Assets", () => { + test("list", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = [ + { + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { name: "Shirt", confidence: 90.12, source: "google-auto-tagging" }, + { name: "T-shirt", confidence: 80.12, source: "aws-auto-tagging" }, + ], + versionInfo: { id: "598821f949c0a938d57563bd", name: "Version 2" }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { brand: "Nike", color: "red" }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + }, + { + fileId: "598821f949c0a938d81963bd", + type: "file-version", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "sale2019"], + AITags: [ + { name: "Shirt", confidence: 90.12, source: "google-auto-tagging" }, + { name: "T-shirt", confidence: 80.12, source: "aws-auto-tagging" }, + ], + versionInfo: { id: "598821f949c0a938d57563bd", name: "Version 1" }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { brand: "Nike", color: "red" }, + createdAt: "2019-08-24T06:15:41.313Z", + updatedAt: "2019-08-24T06:15:41.313Z", + }, + { + type: "folder", + folderId: "6441fce4e809dd54b0dee029", + name: "summer", + folderPath: "/sale", + createdAt: "2023-04-21T03:03:00.869Z", + updatedAt: "2023-04-21T03:10:22.374Z", + }, + ]; + server.mockEndpoint().get("/v1/files").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.assets.list(); + expect(response).toEqual([ + { + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { + name: "Shirt", + confidence: 90.12, + source: "google-auto-tagging", + }, + { + name: "T-shirt", + confidence: 80.12, + source: "aws-auto-tagging", + }, + ], + versionInfo: { + id: "598821f949c0a938d57563bd", + name: "Version 2", + }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { + brand: "Nike", + color: "red", + }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + }, + { + fileId: "598821f949c0a938d81963bd", + type: "file-version", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "sale2019"], + AITags: [ + { + name: "Shirt", + confidence: 90.12, + source: "google-auto-tagging", + }, + { + name: "T-shirt", + confidence: 80.12, + source: "aws-auto-tagging", + }, + ], + versionInfo: { + id: "598821f949c0a938d57563bd", + name: "Version 1", + }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { + brand: "Nike", + color: "red", + }, + createdAt: "2019-08-24T06:15:41.313Z", + updatedAt: "2019-08-24T06:15:41.313Z", + }, + { + type: "folder", + folderId: "6441fce4e809dd54b0dee029", + name: "summer", + folderPath: "/sale", + createdAt: "2023-04-21T03:03:00.869Z", + updatedAt: "2023-04-21T03:10:22.374Z", + }, + ]); + }); +}); diff --git a/tests/wire/cache/invalidation.test.ts b/tests/wire/cache/invalidation.test.ts new file mode 100644 index 00000000..2c2f9d1a --- /dev/null +++ b/tests/wire/cache/invalidation.test.ts @@ -0,0 +1,49 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Invalidation", () => { + test("create", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { url: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg" }; + const rawResponseBody = { requestId: "requestId" }; + server + .mockEndpoint() + .post("/v1/files/purge") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.cache.invalidation.create({ + url: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", + }); + expect(response).toEqual({ + requestId: "requestId", + }); + }); + + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { status: "Pending" }; + server + .mockEndpoint() + .get("/v1/files/purge/requestId") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.cache.invalidation.get("requestId"); + expect(response).toEqual({ + status: "Pending", + }); + }); +}); diff --git a/tests/wire/customMetadataFields.test.ts b/tests/wire/customMetadataFields.test.ts new file mode 100644 index 00000000..77a7a702 --- /dev/null +++ b/tests/wire/customMetadataFields.test.ts @@ -0,0 +1,216 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../mock-server/MockServerPool"; +import { ImageKitClient } from "../../src/Client"; + +describe("CustomMetadataFields", () => { + test("list", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = [ + { + id: "598821f949c0a938d57563dd", + name: "brand", + label: "brand", + schema: { + type: "Text", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "Nike", + isValueRequired: true, + minValue: "minValue", + maxValue: "maxValue", + minLength: 1.1, + maxLength: 1.1, + }, + }, + { + id: "865421f949c0a835d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }, + ]; + server + .mockEndpoint() + .get("/v1/customMetadataFields") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.customMetadataFields.list(); + expect(response).toEqual([ + { + id: "598821f949c0a938d57563dd", + name: "brand", + label: "brand", + schema: { + type: "Text", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "Nike", + isValueRequired: true, + minValue: "minValue", + maxValue: "maxValue", + minLength: 1.1, + maxLength: 1.1, + }, + }, + { + id: "865421f949c0a835d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }, + ]); + }); + + test("create", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "price", + label: "price", + schema: { type: "Number", minValue: 1000, maxValue: 3000 }, + }; + const rawResponseBody = { + id: "598821f949c0a938d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }; + server + .mockEndpoint() + .post("/v1/customMetadataFields") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.customMetadataFields.create({ + name: "price", + label: "price", + schema: { + type: "Number", + minValue: 1000, + maxValue: 3000, + }, + }); + expect(response).toEqual({ + id: "598821f949c0a938d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/v1/customMetadataFields/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.customMetadataFields.delete("id"); + expect(response).toEqual({ + key: "value", + }); + }); + + test("update", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { label: "price", schema: { minValue: 1000, maxValue: 3000 } }; + const rawResponseBody = { + id: "598821f949c0a938d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }; + server + .mockEndpoint() + .patch("/v1/customMetadataFields/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.customMetadataFields.update("id", { + label: "price", + schema: { + minValue: 1000, + maxValue: 3000, + }, + }); + expect(response).toEqual({ + id: "598821f949c0a938d57563dd", + name: "price", + label: "price", + schema: { + type: "Number", + selectOptions: ["small", "medium", "large", 30, 40, true], + defaultValue: "defaultValue", + isValueRequired: true, + minValue: 1000, + maxValue: 3000, + minLength: 1.1, + maxLength: 1.1, + }, + }); + }); +}); diff --git a/tests/wire/files.test.ts b/tests/wire/files.test.ts new file mode 100644 index 00000000..6af296bc --- /dev/null +++ b/tests/wire/files.test.ts @@ -0,0 +1,304 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../mock-server/MockServerPool"; +import { ImageKitClient } from "../../src/Client"; + +describe("Files", () => { + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { name: "Shirt", confidence: 90.12, source: "google-auto-tagging" }, + { name: "T-shirt", confidence: 80.12, source: "aws-auto-tagging" }, + ], + versionInfo: { id: "598821f949c0a938d57563bd", name: "Version 1" }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { brand: "Nike", color: "red" }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + }; + server + .mockEndpoint() + .get("/v1/files/fileId/details") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.get("fileId"); + expect(response).toEqual({ + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { + name: "Shirt", + confidence: 90.12, + source: "google-auto-tagging", + }, + { + name: "T-shirt", + confidence: 80.12, + source: "aws-auto-tagging", + }, + ], + versionInfo: { + id: "598821f949c0a938d57563bd", + name: "Version 1", + }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { + brand: "Nike", + color: "red", + }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + }); + }); + + test("update", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + removeAITags: ["car", "vehicle", "motorsports"], + webhookUrl: "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + 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" }, + ], + tags: ["tag1", "tag2"], + customCoordinates: "10,10,100,100", + customMetadata: { brand: "Nike", color: "red" }, + }; + const rawResponseBody = { + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file1.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { name: "Shirt", confidence: 90.12, source: "google-auto-tagging" }, + { name: "T-shirt", confidence: 80.12, source: "aws-auto-tagging" }, + ], + versionInfo: { id: "598821f949c0a938d57563bd", name: "Version 1" }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { brand: "Nike", color: "red" }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + extensionStatus: { + "google-auto-tagging": "success", + "aws-auto-tagging": "success", + "remove-bg": "pending", + "ai-auto-description": "success", + }, + }; + server + .mockEndpoint() + .patch("/v1/files/fileId/details") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.update("fileId", { + removeAITags: ["car", "vehicle", "motorsports"], + webhookUrl: "https://webhook.site/0d6b6c7a-8e5a-4b3a-8b7c-0d6b6c7a8e5a", + 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", + }, + ], + tags: ["tag1", "tag2"], + customCoordinates: "10,10,100,100", + customMetadata: { + brand: "Nike", + color: "red", + }, + }); + expect(response).toEqual({ + fileId: "598821f949c0a938d57563bd", + type: "file", + name: "file1.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "round-neck", "sale2019"], + AITags: [ + { + name: "Shirt", + confidence: 90.12, + source: "google-auto-tagging", + }, + { + name: "T-shirt", + confidence: 80.12, + source: "aws-auto-tagging", + }, + ], + versionInfo: { + id: "598821f949c0a938d57563bd", + name: "Version 1", + }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { + brand: "Nike", + color: "red", + }, + createdAt: "2019-08-24T06:14:41.313Z", + updatedAt: "2019-08-24T06:14:41.313Z", + extensionStatus: { + "google-auto-tagging": "success", + "aws-auto-tagging": "success", + "remove-bg": "pending", + "ai-auto-description": "success", + }, + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/v1/files/fileId").respondWith().statusCode(200).build(); + + const response = await client.files.delete("fileId"); + expect(response).toEqual(undefined); + }); + + test("copy", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { sourceFilePath: "/path/to/file.jpg", destinationPath: "/folder/to/copy/into/" }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/v1/files/copy") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.copy({ + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/copy/into/", + }); + expect(response).toEqual({ + key: "value", + }); + }); + + test("move", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { sourceFilePath: "/path/to/file.jpg", destinationPath: "/folder/to/move/into/" }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/v1/files/move") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.move({ + sourceFilePath: "/path/to/file.jpg", + destinationPath: "/folder/to/move/into/", + }); + expect(response).toEqual({ + key: "value", + }); + }); + + test("rename", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { filePath: "/path/to/file.jpg", newFileName: "newFileName.jpg" }; + const rawResponseBody = { purgeRequestId: "purgeRequestId" }; + server + .mockEndpoint() + .put("/v1/files/rename") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.rename({ + filePath: "/path/to/file.jpg", + newFileName: "newFileName.jpg", + }); + expect(response).toEqual({ + purgeRequestId: "purgeRequestId", + }); + }); +}); diff --git a/tests/wire/files/bulk.test.ts b/tests/wire/files/bulk.test.ts new file mode 100644 index 00000000..0621d3a0 --- /dev/null +++ b/tests/wire/files/bulk.test.ts @@ -0,0 +1,108 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Bulk", () => { + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"] }; + const rawResponseBody = { successfullyDeletedFileIds: ["successfullyDeletedFileIds"] }; + server + .mockEndpoint() + .post("/v1/files/batch/deleteByFileIds") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.bulk.delete({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + }); + expect(response).toEqual({ + successfullyDeletedFileIds: ["successfullyDeletedFileIds"], + }); + }); + + test("addTags", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], + }; + const rawResponseBody = { successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"] }; + server + .mockEndpoint() + .post("/v1/files/addTags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.bulk.addTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], + }); + expect(response).toEqual({ + successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"], + }); + }); + + test("removeTags", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], + }; + const rawResponseBody = { successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"] }; + server + .mockEndpoint() + .post("/v1/files/removeTags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.bulk.removeTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + tags: ["t-shirt", "round-neck", "sale2019"], + }); + expect(response).toEqual({ + successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"], + }); + }); + + test("removeAITags", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + AITags: ["t-shirt", "round-neck", "sale2019"], + }; + const rawResponseBody = { successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"] }; + server + .mockEndpoint() + .post("/v1/files/removeAITags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.bulk.removeAiTags({ + fileIds: ["598821f949c0a938d57563bd", "598821f949c0a938d57563be"], + AITags: ["t-shirt", "round-neck", "sale2019"], + }); + expect(response).toEqual({ + successfullyUpdatedFileIds: ["successfullyUpdatedFileIds"], + }); + }); +}); diff --git a/tests/wire/files/metadata.test.ts b/tests/wire/files/metadata.test.ts new file mode 100644 index 00000000..8ab2ec5b --- /dev/null +++ b/tests/wire/files/metadata.test.ts @@ -0,0 +1,324 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Metadata", () => { + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + height: 68, + width: 100, + size: 7749, + format: "jpg", + hasColorProfile: true, + quality: 0, + density: 72, + hasTransparency: false, + pHash: "f06830ca9f1e3e90", + bitRate: 1, + duration: 1, + audioCodec: "audioCodec", + videoCodec: "videoCodec", + exif: { + image: { + Make: "Canon", + Model: "Canon EOS 40D", + Orientation: 1, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + Software: "GIMP 2.4.5", + ModifyDate: "2008:07:31 10:38:11", + YCbCrPositioning: 2, + ExifOffset: 214, + GPSInfo: 978, + }, + thumbnail: { + Compression: 6, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + ThumbnailOffset: 1090, + ThumbnailLength: 1378, + }, + exif: { + ExposureTime: 0.00625, + FNumber: 7.1, + ExposureProgram: 1, + ISO: 100, + ExifVersion: "0221", + DateTimeOriginal: "2008:05:30 15:56:01", + CreateDate: "2008:05:30 15:56:01", + ShutterSpeedValue: 7.375, + ApertureValue: 5.625, + ExposureCompensation: 0, + MeteringMode: 5, + Flash: 9, + FocalLength: 135, + SubSecTime: "00", + FlashpixVersion: "0100", + ColorSpace: 1, + ExifImageWidth: 100, + ExifImageHeight: 68, + InteropOffset: 948, + FocalPlaneXResolution: 4438.356164383562, + FocalPlaneYResolution: 4445.969125214408, + FocalPlaneResolutionUnit: 2, + CustomRendered: 0, + ExposureMode: 1, + WhiteBalance: 0, + SceneCaptureType: 0, + }, + gps: { GPSVersionID: [2, 2, 0, 0] }, + interoperability: { InteropIndex: "R98", InteropVersion: "0100" }, + makernote: { key: "value" }, + }, + }; + server + .mockEndpoint() + .get("/v1/files/fileId/metadata") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.metadata.get("fileId"); + expect(response).toEqual({ + height: 68, + width: 100, + size: 7749, + format: "jpg", + hasColorProfile: true, + quality: 0, + density: 72, + hasTransparency: false, + pHash: "f06830ca9f1e3e90", + bitRate: 1, + duration: 1, + audioCodec: "audioCodec", + videoCodec: "videoCodec", + exif: { + image: { + Make: "Canon", + Model: "Canon EOS 40D", + Orientation: 1, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + Software: "GIMP 2.4.5", + ModifyDate: "2008:07:31 10:38:11", + YCbCrPositioning: 2, + ExifOffset: 214, + GPSInfo: 978, + }, + thumbnail: { + Compression: 6, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + ThumbnailOffset: 1090, + ThumbnailLength: 1378, + }, + exif: { + ExposureTime: 0.00625, + FNumber: 7.1, + ExposureProgram: 1, + ISO: 100, + ExifVersion: "0221", + DateTimeOriginal: "2008:05:30 15:56:01", + CreateDate: "2008:05:30 15:56:01", + ShutterSpeedValue: 7.375, + ApertureValue: 5.625, + ExposureCompensation: 0, + MeteringMode: 5, + Flash: 9, + FocalLength: 135, + SubSecTime: "00", + FlashpixVersion: "0100", + ColorSpace: 1, + ExifImageWidth: 100, + ExifImageHeight: 68, + InteropOffset: 948, + FocalPlaneXResolution: 4438.356164383562, + FocalPlaneYResolution: 4445.969125214408, + FocalPlaneResolutionUnit: 2, + CustomRendered: 0, + ExposureMode: 1, + WhiteBalance: 0, + SceneCaptureType: 0, + }, + gps: { + GPSVersionID: [2, 2, 0, 0], + }, + interoperability: { + InteropIndex: "R98", + InteropVersion: "0100", + }, + makernote: { + key: "value", + }, + }, + }); + }); + + test("getFromURL", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + height: 68, + width: 100, + size: 7749, + format: "jpg", + hasColorProfile: true, + quality: 0, + density: 72, + hasTransparency: false, + pHash: "f06830ca9f1e3e90", + bitRate: 1, + duration: 1, + audioCodec: "audioCodec", + videoCodec: "videoCodec", + exif: { + image: { + Make: "Canon", + Model: "Canon EOS 40D", + Orientation: 1, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + Software: "GIMP 2.4.5", + ModifyDate: "2008:07:31 10:38:11", + YCbCrPositioning: 2, + ExifOffset: 214, + GPSInfo: 978, + }, + thumbnail: { + Compression: 6, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + ThumbnailOffset: 1090, + ThumbnailLength: 1378, + }, + exif: { + ExposureTime: 0.00625, + FNumber: 7.1, + ExposureProgram: 1, + ISO: 100, + ExifVersion: "0221", + DateTimeOriginal: "2008:05:30 15:56:01", + CreateDate: "2008:05:30 15:56:01", + ShutterSpeedValue: 7.375, + ApertureValue: 5.625, + ExposureCompensation: 0, + MeteringMode: 5, + Flash: 9, + FocalLength: 135, + SubSecTime: "00", + FlashpixVersion: "0100", + ColorSpace: 1, + ExifImageWidth: 100, + ExifImageHeight: 68, + InteropOffset: 948, + FocalPlaneXResolution: 4438.356164383562, + FocalPlaneYResolution: 4445.969125214408, + FocalPlaneResolutionUnit: 2, + CustomRendered: 0, + ExposureMode: 1, + WhiteBalance: 0, + SceneCaptureType: 0, + }, + gps: { GPSVersionID: [2, 2, 0, 0] }, + interoperability: { InteropIndex: "R98", InteropVersion: "0100" }, + makernote: { key: "value" }, + }, + }; + server.mockEndpoint().get("/v1/files/metadata").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.files.metadata.getFromUrl({ + url: "url", + }); + expect(response).toEqual({ + height: 68, + width: 100, + size: 7749, + format: "jpg", + hasColorProfile: true, + quality: 0, + density: 72, + hasTransparency: false, + pHash: "f06830ca9f1e3e90", + bitRate: 1, + duration: 1, + audioCodec: "audioCodec", + videoCodec: "videoCodec", + exif: { + image: { + Make: "Canon", + Model: "Canon EOS 40D", + Orientation: 1, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + Software: "GIMP 2.4.5", + ModifyDate: "2008:07:31 10:38:11", + YCbCrPositioning: 2, + ExifOffset: 214, + GPSInfo: 978, + }, + thumbnail: { + Compression: 6, + XResolution: 72, + YResolution: 72, + ResolutionUnit: 2, + ThumbnailOffset: 1090, + ThumbnailLength: 1378, + }, + exif: { + ExposureTime: 0.00625, + FNumber: 7.1, + ExposureProgram: 1, + ISO: 100, + ExifVersion: "0221", + DateTimeOriginal: "2008:05:30 15:56:01", + CreateDate: "2008:05:30 15:56:01", + ShutterSpeedValue: 7.375, + ApertureValue: 5.625, + ExposureCompensation: 0, + MeteringMode: 5, + Flash: 9, + FocalLength: 135, + SubSecTime: "00", + FlashpixVersion: "0100", + ColorSpace: 1, + ExifImageWidth: 100, + ExifImageHeight: 68, + InteropOffset: 948, + FocalPlaneXResolution: 4438.356164383562, + FocalPlaneYResolution: 4445.969125214408, + FocalPlaneResolutionUnit: 2, + CustomRendered: 0, + ExposureMode: 1, + WhiteBalance: 0, + SceneCaptureType: 0, + }, + gps: { + GPSVersionID: [2, 2, 0, 0], + }, + interoperability: { + InteropIndex: "R98", + InteropVersion: "0100", + }, + makernote: { + key: "value", + }, + }, + }); + }); +}); diff --git a/tests/wire/files/versions.test.ts b/tests/wire/files/versions.test.ts new file mode 100644 index 00000000..4e26f4a5 --- /dev/null +++ b/tests/wire/files/versions.test.ts @@ -0,0 +1,250 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Versions", () => { + test("list", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = [ + { + fileId: "fileId", + type: "type", + name: "name", + filePath: "filePath", + tags: ["tags"], + AITags: [{}], + versionInfo: { id: "id", name: "name" }, + isPrivateFile: true, + isPublished: true, + customCoordinates: "customCoordinates", + url: "url", + thumbnail: "thumbnail", + fileType: "fileType", + mime: "mime", + width: 1.1, + height: 1.1, + size: 1.1, + hasAlpha: true, + customMetadata: { key: "value" }, + createdAt: "createdAt", + updatedAt: "updatedAt", + }, + ]; + server + .mockEndpoint() + .get("/v1/files/fileId/versions") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.versions.list("fileId"); + expect(response).toEqual([ + { + fileId: "fileId", + type: "type", + name: "name", + filePath: "filePath", + tags: ["tags"], + AITags: [{}], + versionInfo: { + id: "id", + name: "name", + }, + isPrivateFile: true, + isPublished: true, + customCoordinates: "customCoordinates", + url: "url", + thumbnail: "thumbnail", + fileType: "fileType", + mime: "mime", + width: 1.1, + height: 1.1, + size: 1.1, + hasAlpha: true, + customMetadata: { + key: "value", + }, + createdAt: "createdAt", + updatedAt: "updatedAt", + }, + ]); + }); + + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + fileId: "598821f949c0a938d81963bd", + type: "file-version", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "sale2019"], + AITags: [ + { name: "Shirt", confidence: 90.12, source: "google-auto-tagging" }, + { name: "T-shirt", confidence: 80.12, source: "aws-auto-tagging" }, + ], + versionInfo: { id: "598821f949c0a938d57563bd", name: "Version 1" }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { brand: "Nike", color: "red" }, + createdAt: "2019-08-24T06:15:41.313Z", + updatedAt: "2019-08-24T06:15:41.313Z", + }; + server + .mockEndpoint() + .get("/v1/files/fileId/versions/versionId") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.versions.get("fileId", "versionId"); + expect(response).toEqual({ + fileId: "598821f949c0a938d81963bd", + type: "file-version", + name: "file.jpg", + filePath: "/images/products/file.jpg", + tags: ["t-shirt", "sale2019"], + AITags: [ + { + name: "Shirt", + confidence: 90.12, + source: "google-auto-tagging", + }, + { + name: "T-shirt", + confidence: 80.12, + source: "aws-auto-tagging", + }, + ], + versionInfo: { + id: "598821f949c0a938d57563bd", + name: "Version 1", + }, + isPrivateFile: false, + isPublished: true, + url: "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313", + thumbnail: + "https://ik.imagekit.io/your_imagekit_id/images/products/file.jpg?ik-obj-version=iAg8gxkqo_QUBwqNeQrJzuyce2XB7Gc4&updatedAt=1566630881313&tr=n-ik_ml_thumbnail", + fileType: "image", + mime: "image/jpeg", + width: 100, + height: 100, + size: 100, + hasAlpha: false, + customMetadata: { + brand: "Nike", + color: "red", + }, + createdAt: "2019-08-24T06:15:41.313Z", + updatedAt: "2019-08-24T06:15:41.313Z", + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/v1/files/fileId/versions/versionId") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.versions.delete("fileId", "versionId"); + expect(response).toEqual({ + key: "value", + }); + }); + + test("restore", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + fileId: "fileId", + type: "type", + name: "name", + filePath: "filePath", + tags: ["tags"], + AITags: [{ name: "name", confidence: 1.1, source: "source" }], + versionInfo: { id: "id", name: "name" }, + isPrivateFile: true, + isPublished: true, + customCoordinates: "customCoordinates", + url: "url", + thumbnail: "thumbnail", + fileType: "fileType", + mime: "mime", + width: 1.1, + height: 1.1, + size: 1.1, + hasAlpha: true, + customMetadata: { key: "value" }, + createdAt: "createdAt", + updatedAt: "updatedAt", + }; + server + .mockEndpoint() + .put("/v1/files/fileId/versions/versionId/restore") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.files.versions.restore("fileId", "versionId"); + expect(response).toEqual({ + fileId: "fileId", + type: "type", + name: "name", + filePath: "filePath", + tags: ["tags"], + AITags: [ + { + name: "name", + confidence: 1.1, + source: "source", + }, + ], + versionInfo: { + id: "id", + name: "name", + }, + isPrivateFile: true, + isPublished: true, + customCoordinates: "customCoordinates", + url: "url", + thumbnail: "thumbnail", + fileType: "fileType", + mime: "mime", + width: 1.1, + height: 1.1, + size: 1.1, + hasAlpha: true, + customMetadata: { + key: "value", + }, + createdAt: "createdAt", + updatedAt: "updatedAt", + }); + }); +}); diff --git a/tests/wire/folders.test.ts b/tests/wire/folders.test.ts new file mode 100644 index 00000000..bd3b0366 --- /dev/null +++ b/tests/wire/folders.test.ts @@ -0,0 +1,128 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../mock-server/MockServerPool"; +import { ImageKitClient } from "../../src/Client"; + +describe("Folders", () => { + test("create", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { folderName: "summer", parentFolderPath: "/product/images/" }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/v1/folder") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.folders.create({ + folderName: "summer", + parentFolderPath: "/product/images/", + }); + expect(response).toEqual({ + key: "value", + }); + }); + + test("delete", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { folderPath: "/folder/to/delete/" }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/v1/folder") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.folders.delete({ + folderPath: "/folder/to/delete/", + }); + expect(response).toEqual({ + key: "value", + }); + }); + + test("copy", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", + }; + const rawResponseBody = { jobId: "jobId" }; + server + .mockEndpoint() + .post("/v1/bulkJobs/copyFolder") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.folders.copy({ + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", + }); + expect(response).toEqual({ + jobId: "jobId", + }); + }); + + test("move", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", + }; + const rawResponseBody = { jobId: "jobId" }; + server + .mockEndpoint() + .post("/v1/bulkJobs/moveFolder") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.folders.move({ + sourceFolderPath: "/path/of/source/folder", + destinationPath: "/path/of/destination/folder", + }); + expect(response).toEqual({ + jobId: "jobId", + }); + }); + + test("rename", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + const rawRequestBody = { folderPath: "/path/of/folder", newFolderName: "new-folder-name" }; + const rawResponseBody = { jobId: "jobId" }; + server + .mockEndpoint() + .post("/v1/bulkJobs/renameFolder") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.folders.rename({ + folderPath: "/path/of/folder", + newFolderName: "new-folder-name", + }); + expect(response).toEqual({ + jobId: "jobId", + }); + }); +}); diff --git a/tests/wire/folders/job.test.ts b/tests/wire/folders/job.test.ts new file mode 100644 index 00000000..1e6188e2 --- /dev/null +++ b/tests/wire/folders/job.test.ts @@ -0,0 +1,29 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import { mockServerPool } from "../../mock-server/MockServerPool"; +import { ImageKitClient } from "../../../src/Client"; + +describe("Job", () => { + test("get", async () => { + const server = mockServerPool.createServer(); + const client = new ImageKitClient({ username: "test", password: "test", environment: server.baseUrl }); + + const rawResponseBody = { + jobId: "5d5b1a9b4c8c4c0001f3e4a2", + type: "COPY_FOLDER", + status: "Completed", + purgeRequestId: "purgeRequestId", + }; + server.mockEndpoint().get("/v1/bulkJobs/jobId").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const response = await client.folders.job.get("jobId"); + expect(response).toEqual({ + jobId: "5d5b1a9b4c8c4c0001f3e4a2", + type: "COPY_FOLDER", + status: "Completed", + purgeRequestId: "purgeRequestId", + }); + }); +}); diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..c75083dc --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "extendedDiagnostics": true, + "strict": true, + "target": "ES6", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "baseUrl": "src" + }, + "include": ["src"], + "exclude": [] +} diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 00000000..5c11446f --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs" + }, + "include": ["src"], + "exclude": [] +} diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 00000000..95a5eb73 --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm" + }, + "include": ["src"], + "exclude": [] +} diff --git a/tsconfig.json b/tsconfig.json index 69e4ceab..d77fdf00 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,74 +1,3 @@ { - "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 + "extends": "./tsconfig.cjs.json" +} diff --git a/utils/authorization.ts b/utils/authorization.ts deleted file mode 100644 index 231c1672..00000000 --- a/utils/authorization.ts +++ /dev/null @@ -1,16 +0,0 @@ -import FormData from "form-data"; - -interface RequestOptions { - url: string; - headers?: Record; - method: string; - formData?: FormData; - qs?: Object; - json?: any; - auth?: { - user: string; - pass: string; - }; -} - -export type { RequestOptions }; diff --git a/utils/hamming-distance.d.ts b/utils/hamming-distance.d.ts deleted file mode 100644 index 7fa5d2c3..00000000 --- a/utils/hamming-distance.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "hamming-distance"; diff --git a/utils/phash.ts b/utils/phash.ts deleted file mode 100644 index dc2f3d8d..00000000 --- a/utils/phash.ts +++ /dev/null @@ -1,30 +0,0 @@ -// import packages -import compare from "hamming-distance"; -// import constants -import errors from "../libs/constants/errorMessages"; - -// regexp validator -const hexRegExp = new RegExp(/^[0-9a-fA-F]+$/, "i"); - -const errorHandler = (error: { message: string; help: string }): Error => new Error(`${error.message}: ${error.help}`); - -const pHashDistance = (firstHash: string, secondHash: string): number | Error => { - if (!firstHash || !secondHash) { - return errorHandler(errors.MISSING_PHASH_VALUE); - } - if (!hexRegExp.test(firstHash) || !hexRegExp.test(secondHash)) { - return errorHandler(errors.INVALID_PHASH_VALUE); - } - - const firstHashString = firstHash.toString(); - const secondHashString = secondHash.toString(); - - if (firstHashString.length !== secondHashString.length) { - return errorHandler(errors.UNEQUAL_STRING_LENGTH); - } - - const distance = compare(firstHashString, secondHashString); - return distance; -}; - -export default { pHashDistance }; diff --git a/utils/request.ts b/utils/request.ts deleted file mode 100644 index aa1bf855..00000000 --- a/utils/request.ts +++ /dev/null @@ -1,91 +0,0 @@ -import respond from "./respond"; -import { RequestOptions } from "./authorization"; -import { ImageKitOptions } from "../libs/interfaces"; -import { IKCallback } from "../libs/interfaces/IKCallback"; -import axios, { AxiosError, AxiosHeaders, AxiosRequestConfig, AxiosResponse } from "axios"; - -// constant -const UnknownError: string = "Unknown error occured"; - -export default function request( - requestOptions: RequestOptions, - defaultOptions: ImageKitOptions, - callback?: IKCallback, -) { - - var options: AxiosRequestConfig = { - method: requestOptions.method, - url: requestOptions.url, - auth: { - username: defaultOptions.privateKey || "", - password: "", - }, - maxBodyLength: Infinity, - }; - - if (typeof requestOptions.json === "object") options.data = requestOptions.json; - else if (typeof requestOptions.formData === "object") options.data = requestOptions.formData; - - if (typeof requestOptions.qs === "object") options.params = requestOptions.qs; - if (typeof requestOptions.headers === "object") options.headers = requestOptions.headers; - - axios(options).then((response: AxiosResponse) => { - if (typeof callback !== "function") return; - const { data, status, headers } = response; - const responseMetadata = { - statusCode: status, - headers: (headers as AxiosHeaders).toJSON() - } - let result = data ? data : {} as T; - // define status code and headers as non-enumerable properties on data - Object.defineProperty(result, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - respond(false, result, callback); - }, (error: AxiosError) => { - if (typeof callback !== "function") return; - if (error.response) { - // The request was made and the server responded with a status code - // that falls out of the range of 2xx - const responseMetadata = { - statusCode: error.response.status, - headers: (error.response.headers as AxiosHeaders).toJSON() - } - - let result = {} as Object; - if (error.response.data && typeof error.response.data === "object") { - result = error.response.data - } else if (error.response.data && typeof error.response.data === "string") { - result = { - help: error.response.data - } - } - - if (error.response.status === 429) { - result = { - ...result, - "X-RateLimit-Limit": parseInt(error.response.headers["x-ratelimit-limit"], 10), - "X-RateLimit-Reset": parseInt(error.response.headers["x-ratelimit-reset"], 10), - "X-RateLimit-Interval": parseInt(error.response.headers["x-ratelimit-interval"], 10), - } - } - // define status code and headers as non-enumerable properties on data - Object.defineProperty(result, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - respond(true, result, callback); - - } else if (error) { - respond(true, error, callback); - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - } else { - respond(true, new Error(UnknownError), callback); - } - }) -} diff --git a/utils/respond.ts b/utils/respond.ts deleted file mode 100644 index 96c359e3..00000000 --- a/utils/respond.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IKCallback } from "../libs/interfaces/IKCallback"; - -export default function respond(isError: boolean, response: any, callback?: IKCallback) { - if (typeof callback === "function") { - if (isError) { - callback(response, null); - } else { - callback(null, response); - } - } -} diff --git a/utils/transformation.ts b/utils/transformation.ts deleted file mode 100644 index 5b89e51a..00000000 --- a/utils/transformation.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - VARIABLES -*/ -import supportedTransforms from "../libs/constants/supportedTransforms"; -import { UrlOptions, TransformationPosition } from "../libs/interfaces"; - -const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = "path"; -const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; - -const CHAIN_TRANSFORM_DELIMITER: string = ":"; -const TRANSFORM_DELIMITER: string = ","; -const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; - -const getDefault = function (): TransformationPosition { - return DEFAULT_TRANSFORMATION_POSITION; -}; - -const addAsQueryParameter = function (options: UrlOptions): boolean { - return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; -}; - -const getTransformKey = function (transform: string): string { - if (!transform) { - return ""; - } - - return supportedTransforms[transform] || supportedTransforms[transform.toLowerCase()] || ""; -}; - -const getChainTransformDelimiter = function (): string { - return CHAIN_TRANSFORM_DELIMITER; -}; - -const getTransformDelimiter = function (): string { - return TRANSFORM_DELIMITER; -}; - -const getTransformKeyValueDelimiter = function (): string { - return TRANSFORM_KEY_VALUE_DELIMITER; -}; - -export default { - getDefault, - addAsQueryParameter, - getTransformKey, - getChainTransformDelimiter, - getTransformDelimiter, - getTransformKeyValueDelimiter, -}; diff --git a/utils/urlFormatter.ts b/utils/urlFormatter.ts deleted file mode 100644 index 96e2b158..00000000 --- a/utils/urlFormatter.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Adds a leading slash to the given string if it does not already start with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with a leading slash if it was missing. - */ -const addLeadingSlash = function (str: string) { - // Check if the input is a string and does not start with a slash - if (typeof str === "string" && str[0] !== "/") { - // Prepend a slash to the string - str = "/" + str; - } - - // Return the processed string - return str; -}; - - -/** - * Removes the leading slash from the given string if it starts with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with the leading slash removed if it was present. - */ -const removeLeadingSlash = function (str: string) { - // Check if the input is a string and starts with a slash - if (typeof str === "string" && str[0] === "/") { - // Remove the leading slash from the string - str = str.substring(1); - } - - // Return the processed string - return str; -}; - - -/** - * Removes the trailing slash from the given string if it ends with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with the trailing slash removed if it was present. - */ -const removeTrailingSlash = function (str: string) { - // Check if the input is a string and ends with a slash - if (typeof str === "string" && str[str.length - 1] === "/") { - // Remove the trailing slash from the string - str = str.substring(0, str.length - 1); - } - - // Return the processed string - return str; -}; - - -/** - * Adds a trailing slash to the given string if it does not already end with one. - * - * @param {string} str - The input string to be processed. - * @returns {string} - The modified string with a trailing slash if it was missing. - */ -const addTrailingSlash = function (str: string) { - // Check if the input is a string and does not end with a slash - if (typeof str === "string" && str[str.length - 1] !== "/") { - // Append a trailing slash to the string - str = str + "/"; - } - - // Return the processed string - return str; -}; - - -export default { addLeadingSlash, removeLeadingSlash, removeTrailingSlash, addTrailingSlash }; diff --git a/utils/webhook-signature.ts b/utils/webhook-signature.ts deleted file mode 100644 index 24028825..00000000 --- a/utils/webhook-signature.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { createHmac } from "crypto"; -import { isNaN } from "lodash"; -import errorMessages from "../libs/constants/errorMessages"; -import type { WebhookEvent } from "../libs/interfaces"; - -/** - * @description Enum for Webhook signature item names - */ -enum SignatureItems { - Timestamp = "t", - V1 = "v1", -} - -const HASH_ALGORITHM = "sha256"; - -/** - * @param timstamp - Webhook request timestamp - * @param payload - Webhook payload as UTF8 encoded string - * @param secret - Webhook secret as UTF8 encoded string - * @returns Hmac with webhook secret as key and `${timestamp}.${payload}` as hash payload. - */ -const computeHmac = ( - timstamp: Date, - payload: string, - secret: string -): string => { - const hashPayload = `${timstamp.getTime()}.${payload}`; - return createHmac(HASH_ALGORITHM, secret).update(hashPayload).digest("hex"); -}; - -/** - * @description Extract items from webhook signature string - */ -const deserializeSignature = ( - signature: string -): { - timestamp: number; - v1: string; -} => { - const items = signature.split(","); - const itemMap = items.map((item) => item.split("=")); // eg. [["t", 1656921250765], ["v1", 'afafafafafaf']] - const timestampString = itemMap.find( - ([key]) => key === SignatureItems.Timestamp - )?.[1]; // eg. 1656921250765 - - // parse timestamp - if (timestampString === undefined) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_TIMESTAMP_MISSING.message - ); - } - const timestamp = parseInt(timestampString, 10); - if (isNaN(timestamp) || timestamp < 0) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_TIMESTAMP_INVALID.message - ); - } - - // parse v1 signature - const v1 = itemMap.find(([key]) => key === SignatureItems.V1)?.[1]; // eg. 'afafafafafaf' - if (v1 === undefined) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_SIGNATURE_MISSING.message - ); - } - - return { timestamp, v1 }; -}; - -/** - * @param payload - Raw webhook request body (Encoded as UTF8 string or Buffer) - * @param signature - Webhook signature as UTF8 encoded strings (Stored in `x-ik-signature` header of the request) - * @param secret - Webhook secret as UTF8 encoded string [Copy from ImageKit dashboard](https://imagekit.io/dashboard/developer/webhooks) - * @returns \{ `timstamp`: Verified UNIX epoch timestamp if signature, `event`: Parsed webhook event payload \} - */ -export const verify = ( - payload: string | Uint8Array, - signature: string, - secret: string -): { - timestamp: number; - event: WebhookEvent; -} => { - const { timestamp, v1 } = deserializeSignature(signature); - const payloadAsString: string = - typeof payload === "string" - ? payload - : Buffer.from(payload).toString("utf8"); - const computedHmac = computeHmac( - new Date(timestamp), - payloadAsString, - secret - ); - if (v1 !== computedHmac) { - throw new Error( - errorMessages.VERIFY_WEBHOOK_EVENT_SIGNATURE_INCORRECT.message - ); - } - return { - timestamp, - event: JSON.parse(payloadAsString) as WebhookEvent, - }; -}; diff --git a/yarn.lock b/yarn.lock index f033c13b..056bcdcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,438 +2,127 @@ # 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== - dependencies: - "@jridgewell/gen-mapping" "^0.1.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" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@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== - dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" - 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== +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" -"@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== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: - "@babel/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-validator-identifier" "^7.27.1" 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" + picocolors "^1.1.1" + +"@babel/compat-data@^7.27.2": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" + integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.3.tgz#aceddde69c5d1def69b839d09efa3e3ff59c97cb" + integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helpers" "^7.28.3" + "@babel/parser" "^7.28.3" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.3" + "@babel/types" "^7.28.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.28.3", "@babel/generator@^7.7.2": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" + integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw== + dependencies: + "@babel/parser" "^7.28.3" + "@babel/types" "^7.28.2" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== + dependencies: + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-module-transforms@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.28.3" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helpers@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.3.tgz#b83156c0a2232c133d1b535dd5d3452119c7e441" + integrity sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw== + dependencies: + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.2" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" + integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== + dependencies: + "@babel/types" "^7.28.2" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -442,6 +131,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" @@ -456,26 +152,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-dynamic-import@^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== - 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-import-attributes@^7.24.7": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.27.1" -"@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.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -484,6 +173,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -540,586 +236,706 @@ 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== +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/template@^7.27.2", "@babel/template@^7.3.3": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" + +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" + integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.3" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.3" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.2" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.3.3": + version "7.28.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@bundled-es-modules/cookie@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz#b41376af6a06b3e32a15241d927b840a9b4de507" + integrity sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + cookie "^0.7.2" -"@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== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" +"@bundled-es-modules/statuses@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz#761d10f44e51a94902c4da48675b71a76cc98872" + integrity sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg== + dependencies: + statuses "^2.0.1" + +"@bundled-es-modules/tough-cookie@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz#fa9cd3cedfeecd6783e8b0d378b4a99e52bde5d3" + integrity sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw== + dependencies: + "@types/tough-cookie" "^4.0.5" + tough-cookie "^4.1.4" + +"@inquirer/confirm@^5.0.0": + version "5.1.14" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.14.tgz#e6321edf51a3a5f54dc548b80ef6ba89891351ad" + integrity sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q== + dependencies: + "@inquirer/core" "^10.1.15" + "@inquirer/type" "^3.0.8" + +"@inquirer/core@^10.1.15": + version "10.1.15" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.1.15.tgz#8feb69fd536786181a2b6bfb84d8674faa9d2e59" + integrity sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA== + dependencies: + "@inquirer/figures" "^1.0.13" + "@inquirer/type" "^3.0.8" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + mute-stream "^2.0.0" + signal-exit "^4.1.0" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" -"@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" +"@inquirer/figures@^1.0.13": + version "1.0.13" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.13.tgz#ad0afd62baab1c23175115a9b62f511b6a751e45" + integrity sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw== + +"@inquirer/type@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.8.tgz#efc293ba0ed91e90e6267f1aacc1c70d20b8b4e8" + integrity sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw== -"@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== +"@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", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@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== +"@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-plugin-utils" "^7.17.12" + "@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-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== +"@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-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" - -"@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== + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + jest-get-type "^29.6.3" -"@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" +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@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== +"@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-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@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-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== +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.30" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" + integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mswjs/interceptors@^0.39.1": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.39.6.tgz#44094a578f20da4749d1a0eaf3cdb7973604004b" + integrity sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw== + dependencies: + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/logger" "^0.3.0" + "@open-draft/until" "^2.0.0" + is-node-process "^1.2.0" + outvariant "^1.4.3" + strict-event-emitter "^0.5.1" + +"@open-draft/deferred-promise@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd" + integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA== -"@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== +"@open-draft/logger@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954" + integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + is-node-process "^1.2.0" + outvariant "^1.4.0" + +"@open-draft/until@^2.0.0", "@open-draft/until@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" + integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== -"@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== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: - "@babel/helper-plugin-utils" "^7.17.12" + type-detect "4.0.8" -"@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== +"@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: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@sinonjs/commons" "^3.0.0" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@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== +"@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/helper-plugin-utils" "^7.17.12" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" -"@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== +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/types" "^7.0.0" -"@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== +"@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/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/parser" "^7.1.0" + "@babel/types" "^7.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== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== 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" + "@babel/types" "^7.28.2" + +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== -"@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== +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== 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" + "@types/eslint" "*" + "@types/estree" "*" -"@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== +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@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== +"@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: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" + "@types/node" "*" -"@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== +"@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: - "@babel/helper-plugin-utils" "^7.17.12" + "@types/istanbul-lib-coverage" "*" -"@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== +"@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: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@types/istanbul-lib-report" "*" -"@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" +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" -"@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== +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== 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" + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" -"@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" +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@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== +"@types/node@*": + version "24.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" + integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" + undici-types "~7.10.0" -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== +"@types/node@^18.19.70": + version "18.19.123" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.123.tgz#08a3e4f5e0c73b8840c677b7635ce59d5dc1f76d" + integrity sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg== 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" + undici-types "~5.26.4" -"@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== +"@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/statuses@^2.0.4": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa" + integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA== + +"@types/tough-cookie@*", "@types/tough-cookie@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + +"@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== -"@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== +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@types/yargs-parser" "*" -"@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== +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" -"@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== +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== -"@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== +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== -"@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== +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== -"@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== +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" -"@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== +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== -"@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== +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: - type-detect "4.0.8" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" -"@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== +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@xtuc/ieee754" "^1.2.0" -"@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== +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: - "@sinonjs/commons" "^1.6.0" - lodash.get "^4.4.2" - type-detect "^4.0.8" - -"@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== + "@xtuc/long" "4.2.2" -"@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== +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== -"@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== +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@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== +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" -"@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== +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" -"@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== +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" -"@types/node@*": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" - integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== -"@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== +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -"@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== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -"@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== +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== dependencies: - "@types/sinonjs__fake-timers" "*" - -"@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== + acorn "^8.1.0" + acorn-walk "^8.0.2" -"@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== +acorn-import-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== -"@types/uuid@^8.3.4": +acorn-walk@^8.0.2: 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== + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" -"@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== +acorn@^8.1.0, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.15.0, acorn@^8.8.1: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== agent-base@6: version "6.0.2" @@ -1128,36 +944,42 @@ agent-base@6: dependencies: debug "4" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" + ajv "^8.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-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" -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== +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -1165,26 +987,19 @@ 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== + +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== - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1192,181 +1007,150 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -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.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" + integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== 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.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-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" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: 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== +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.24.0: + version "4.25.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.2.tgz#90c1507143742d743544ae6e92bca3348adff667" + integrity sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA== dependencies: - fill-range "^7.0.1" + caniuse-lite "^1.0.30001733" + electron-to-chromium "^1.5.199" + node-releases "^2.0.19" + update-browserslist-db "^1.1.3" -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.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" -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: +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + es-errors "^1.3.0" + function-bind "^1.1.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: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" +caniuse-lite@^1.0.30001733: + version "1.0.30001735" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz#ba658fd3fd24a4106fd68d5ce472a2c251494dbe" + integrity sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w== chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" @@ -1376,90 +1160,49 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: +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== -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== +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" + integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" 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== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.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== -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^2.0.1: version "2.0.1" @@ -1468,276 +1211,326 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -combined-stream@^1.0.6, combined-stream@^1.0.8: +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@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 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== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +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: - 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== + "@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" + +cross-spawn@^7.0.3: + 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== +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== -debug@4, debug@^4.1.0, debug@^4.1.1: - 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" +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: - ms "2.1.2" + cssom "~0.3.6" -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== +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== dependencies: - type-detect "^4.0.0" + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" -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== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== dependencies: - strip-bom "^4.0.0" + ms "^2.1.3" -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" +decimal.js@^10.4.2: + version "10.6.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" + integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== + +dedent@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" + integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== + +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== -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== +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@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +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== -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== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +electron-to-chromium@^1.5.199: + version "1.5.203" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz#ef7fc2f7e1b816fa4535c861d1ec1348204142b6" + integrity sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g== + +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== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.3: + version "5.18.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44" + integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" -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== +entities@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== + +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-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" + is-arrayish "^0.2.1" -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== +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -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== +es-module-lexer@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== -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== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-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== -esprima@^4.0.0: +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +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@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +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== - dependencies: - punycode "^1.3.2" +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -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== +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: - to-regex-range "^5.0.1" - -find-cache-dir@^2.0.0: + 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.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-json-stable-stringify@2.x, 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== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -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== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== -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== +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: - locate-path "^6.0.0" - path-exists "^4.0.0" + bser "2.1.1" -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 +1540,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== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - 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== + version "4.0.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" 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== - -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== - 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-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-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== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" -glob-parent@~5.1.0, 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" +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@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - 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" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -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,102 +1627,84 @@ 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" -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== - -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== - -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== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, 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== + +graphql@^16.8.1: + version "16.11.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" + integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== + +handlebars@^4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-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-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -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== +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - function-bind "^1.1.1" + has-symbols "^1.0.3" -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.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 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== +headers-polyfill@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.3.tgz#922a0155de30ecc1f785bcf04be77844ca95ad07" + integrity sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ== -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== +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== dependencies: - parse-passwd "^1.0.0" + whatwg-encoding "^2.0.0" 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== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: - "@tootallnate/once" "1" + "@tootallnate/once" "2" agent-base "6" debug "4" -https-proxy-agent@^5.0.0: +https-proxy-agent@^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== @@ -1994,23 +1712,31 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -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== +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: - minimatch "^3.0.4" + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2024,214 +1750,87 @@ inherits@2: 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.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: - has-tostringtag "^1.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== + hasown "^2.0.2" 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: - 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-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-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-node-process@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" + integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== 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-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== 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.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: - archy "^1.0.0" - cross-spawn "^7.0.3" + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" 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 +1842,401 @@ 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.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +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-jsdom@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jsdom "^20.0.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.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@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +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.7.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,50 +2244,77 @@ 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== - dependencies: - argparse "^2.0.1" +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + 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== -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-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -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== +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== -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +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== -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== +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== -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== +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@^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== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== locate-path@^5.0.0: version "5.0.0" @@ -2316,80 +2323,71 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.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== - -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@^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.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -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== +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: - chalk "^4.0.0" + yallist "^3.0.2" -loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== +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: - get-func-name "^2.0.0" + semver "^7.5.3" -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== +make-error@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: - pify "^4.0.1" - semver "^5.6.0" + tmpl "1.0.5" -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== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +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== + +micromatch@^4.0.0, 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: - semver "^6.0.0" + braces "^3.0.3" + picomatch "^2.3.1" 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== -mime-types@^2.1.12: +mime-types@^2.1.12, mime-types@^2.1.27: 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== dependencies: mime-db "1.52.0" -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: version "3.1.2" @@ -2398,167 +2396,81 @@ minimatch@^3.0.4, minimatch@^3.1.1: 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" - -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== +minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -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== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.21" - propagate "^2.0.0" +msw@^2.8.4: + version "2.10.5" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.10.5.tgz#3e43f12e97581c260bf38d8817732b9fec3bfdb0" + integrity sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A== + dependencies: + "@bundled-es-modules/cookie" "^2.0.1" + "@bundled-es-modules/statuses" "^1.0.1" + "@bundled-es-modules/tough-cookie" "^0.1.6" + "@inquirer/confirm" "^5.0.0" + "@mswjs/interceptors" "^0.39.1" + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/until" "^2.1.0" + "@types/cookie" "^0.6.0" + "@types/statuses" "^2.0.4" + graphql "^16.8.1" + headers-polyfill "^4.0.2" + is-node-process "^1.2.0" + outvariant "^1.4.3" + path-to-regexp "^6.3.0" + picocolors "^1.1.1" + strict-event-emitter "^0.5.1" + type-fest "^4.26.1" + yargs "^17.7.2" + +mute-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" + integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== -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== - dependencies: - whatwg-url "^5.0.0" +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -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.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== -normalize-path@^3.0.0, 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== - 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" - -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== - -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-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: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" + path-key "^3.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== - dependencies: - array.prototype.reduce "^1.0.4" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" +nwsapi@^2.2.2: + version "2.2.21" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.21.tgz#8df7797079350adda208910d8c33fc4c2d7520c3" + integrity sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA== once@^1.3.0: version "1.4.0" @@ -2567,27 +2479,32 @@ 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" + +outvariant@^1.4.0, outvariant@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873" + integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== + +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" -p-locate@^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" @@ -2595,44 +2512,27 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-map@^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== - dependencies: - aggregate-error "^3.0.0" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -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== +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: - graceful-fs "^4.1.15" - hasha "^5.0.0" - lodash.flattendeep "^4.4.0" - release-zalgo "^1.0.0" + "@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" -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== - -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@^7.0.0, parse5@^7.1.1: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" path-exists@^4.0.0: version "4.0.0" @@ -2644,7 +2544,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 +2554,173 @@ 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" +path-to-regexp@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== -pathval@^1.1.1: +picocolors@^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== + 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.1: +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== - -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" +pirates@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== -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== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -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.4.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== -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== + kleur "^3.0.3" + sisteransi "^1.0.5" -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== - -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== +psl@^1.1.33: + version "1.15.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== dependencies: - "@babel/runtime" "^7.8.4" + punycode "^2.3.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.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -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.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== -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== +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: - jsesc "~0.5.0" + safe-buffer "^5.1.0" -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== - dependencies: - es6-error "^4.0.1" +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== 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== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +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@^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.3" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== + +resolve@^1.20.0: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.16.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== - dependencies: - glob "^7.1.3" - -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== - dependencies: - tslib "^1.9.0" - safe-buffer@^5.1.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== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" -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== +schema-utils@^4.3.0, schema-utils@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.2.tgz#0c10878bf4a73fd2b1dfd14b9462b26788c806ae" + integrity sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ== dependencies: - randombytes "^2.1.0" + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.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@^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== -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== +semver@^7.3.4, semver@^7.5.3, semver@^7.5.4, semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: - kind-of "^6.0.2" + randombytes "^2.1.0" shebang-command@^2.0.0: version "2.0.0" @@ -2901,38 +2734,35 @@ 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" +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" -source-map-support@^0.5.16: +source-map-support@~0.5.20: 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== @@ -2940,49 +2770,47 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -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" +source-map@^0.7.4: + version "0.7.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.6.tgz#a3658ab87e5b6429c8a1f3ba0083d4c61ca3ef02" + integrity sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ== 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== +statuses@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +strict-event-emitter@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" + integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== + +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-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,31 +2819,6 @@ 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== - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3028,30 +2831,16 @@ strip-bom@^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" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.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" @@ -3059,21 +2848,48 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -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== - 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" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" + integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== + +terser-webpack-plugin@^5.3.11: + version "5.3.14" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz#9031d48e57ab27567f02ace85c7d690db66c3e06" + integrity sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + serialize-javascript "^6.0.2" + terser "^5.31.1" + +terser@^5.31.1: + version "5.43.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d" + integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.14.0" + commander "^2.20.0" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -3084,10 +2900,10 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -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" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-regex-range@^5.0.1: version "5.0.1" @@ -3096,155 +2912,208 @@ 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== - -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== +tough-cookie@^4.1.2, tough-cookie@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + +ts-jest@^29.3.4: + version "29.4.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.1.tgz#42d33beb74657751d315efb9a871fe99e3b9b519" + integrity sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw== + dependencies: + bs-logger "^0.2.6" + fast-json-stable-stringify "^2.1.0" + handlebars "^4.7.8" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.2" + type-fest "^4.41.0" + yargs-parser "^21.1.1" + +ts-loader@^9.5.1: + version "9.5.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.2.tgz#1f3d7f4bb709b487aaa260e8f19b301635d08020" + integrity sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" -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== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^4.26.1, type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + +typescript@~5.7.2: + version "5.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== + +uglify-js@^3.1.4: + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + +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@~7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" + integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +update-browserslist-db@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== dependencies: - 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== + escalade "^3.2.0" + picocolors "^1.1.1" -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== +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== 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== + querystringify "^2.1.1" + requires-port "^1.0.0" -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== +v8-to-istanbul@^9.0.1: + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: - 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== + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" -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== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + xml-name-validator "^4.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== +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: - fast-url-parser "^1.1.3" + makeerror "1.0.12" -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== - -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== +watchpack@^2.4.1: + version "2.4.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.4.tgz#473bda72f0850453da6425081ea46fc0d7602947" + integrity sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA== dependencies: - homedir-polyfill "^1.0.1" + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" -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== - -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== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +webpack-sources@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" + integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== + +webpack@^5.97.1: + version "5.101.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.101.2.tgz#08c222b7acfce7da95c593e2f88ea1638a07b344" + integrity sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.15.0" + acorn-import-phases "^1.0.3" + browserslist "^4.24.0" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.3" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^4.3.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.11" + watchpack "^2.4.1" + webpack-sources "^3.3.3" + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" + iconv-lite "0.6.3" -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" +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -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== +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" -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== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== wrap-ansi@^6.2.0: version "6.2.0" @@ -3269,85 +3138,63 @@ 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" + signal-exit "^3.0.7" -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== +ws@^8.11.0: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== 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== - -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" - -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== +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-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.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: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +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 "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" + string-width "^4.2.3" 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== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - 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" + yargs-parser "^21.1.1" yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==