From f485cd7d7b69adb87297b974dfe093287f23665a Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 22 Mar 2023 14:59:49 +0100 Subject: [PATCH 001/166] Fix URLSearchParams append instead of set Via https://github.com/imagekit-developer/imagekit-javascript/issues/8 --- src/utils/request.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/request.ts b/src/utils/request.ts index dc351f2..729c6c2 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -66,7 +66,7 @@ export const generateSignatureToken = ( var xhr = new XMLHttpRequest(); xhr.timeout = 60000; var urlObj = new URL(authenticationEndpoint); - urlObj.searchParams.set("t", Math.random().toString()); + urlObj.searchParams.append("t", Math.random().toString()); xhr.open('GET', urlObj.toString()); xhr.ontimeout = function (e) { return reject(errorMessages.AUTH_ENDPOINT_TIMEOUT); @@ -128,4 +128,4 @@ export const uploadFile = ( }; uploadFileXHR.send(formData); }); -} \ No newline at end of file +} From 0ac3953503c176ce3799b1537234a89e3e9eaca6 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 23 Mar 2023 12:48:56 +0530 Subject: [PATCH 002/166] bump version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40d6d1e..1c9edc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "1.5.4", + "version": "1.5.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index cef6c5d..7832b5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "1.5.4", + "version": "1.5.5", "description": "Javascript SDK for using ImageKit.io in the browser", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 23e218aacc807d83eebf799a58c654b55ff8ebcf Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Wed, 19 Jul 2023 13:08:26 +0530 Subject: [PATCH 003/166] fix: remove authenticationEndpoint and logic --- src/interfaces/ImageKitOptions.ts | 3 +- src/interfaces/UploadOptions.ts | 14 ++++++++ src/upload/index.ts | 14 ++++---- src/utils/request.ts | 54 ++----------------------------- test/data/index.js | 1 - test/initialization.js | 1 - 6 files changed, 25 insertions(+), 62 deletions(-) diff --git a/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts index 2ba4bcd..d7394f0 100644 --- a/src/interfaces/ImageKitOptions.ts +++ b/src/interfaces/ImageKitOptions.ts @@ -4,6 +4,5 @@ export interface ImageKitOptions { urlEndpoint: string; sdkVersion?: string; publicKey?: string; - authenticationEndpoint?: string; transformationPosition?: TransformationPosition; -} \ No newline at end of file +} diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 24f18ec..3e478e9 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -13,6 +13,20 @@ export interface UploadOptions { * Pass the full URL, for example - https://www.example.com/rest-of-the-image-path.jpg. */ file: string | Blob | File; + /** + * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. Learn how to create a signature below on the page. This should be in lowercase. + * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. + */ + signature: string; + /** + * A unique value generated by the client, which will be used by the ImageKit.io server 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. + * 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; + /** + * The time until your signature is valid. It must be a Unix time in less than 1 hour into the future. It should be in seconds. + */ + expire: number; /** * The name with which the file has to be uploaded. * The file name can contain: diff --git a/src/upload/index.ts b/src/upload/index.ts index 8fabb9b..2aa13d2 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -19,16 +19,10 @@ export const upload = ( return; } - if (!options.authenticationEndpoint) { - respond(true, errorMessages.MISSING_AUTHENTICATION_ENDPOINT, callback); - return; - } - if (!options.publicKey) { respond(true, errorMessages.MISSING_PUBLIC_KEY, callback); return; } - var formData = new FormData(); let key: keyof typeof uploadOptions; for (key in uploadOptions) { @@ -46,11 +40,17 @@ export const upload = ( formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); } else if(uploadOptions[key] !== undefined) { formData.append(key, String(uploadOptions[key])); + } else if (key === 'signature') { + formData.append("signature", uploadOptions.signature); + } else if (key === 'expire') { + formData.append("expire", String(uploadOptions.expire)); + } else if (key === 'token') { + formData.append("token", uploadOptions.token); } } } formData.append("publicKey", options.publicKey); - request(xhr, formData, { ...options, authenticationEndpoint: options.authenticationEndpoint }, callback); + request(xhr, formData, callback); }; diff --git a/src/utils/request.ts b/src/utils/request.ts index 729c6c2..fd7688d 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -41,63 +41,15 @@ const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse void) => { - - generateSignatureToken(options.authenticationEndpoint).then((signaturObj) => { - formData.append("signature", signaturObj.signature); - formData.append("expire", String(signaturObj.expire)); - formData.append("token", signaturObj.token); - - uploadFile(uploadFileXHR, formData).then((result) => { - return respond(false, result, callback); - }, (ex) => { - return respond(true, ex, callback); - }); + + uploadFile(uploadFileXHR, formData).then((result) => { + return respond(false, result, callback); }, (ex) => { return respond(true, ex, callback); }); } -export const generateSignatureToken = ( - authenticationEndpoint: string -): Promise => { - return new Promise((resolve, reject) => { - var xhr = new XMLHttpRequest(); - xhr.timeout = 60000; - var urlObj = new URL(authenticationEndpoint); - urlObj.searchParams.append("t", Math.random().toString()); - xhr.open('GET', urlObj.toString()); - xhr.ontimeout = function (e) { - return reject(errorMessages.AUTH_ENDPOINT_TIMEOUT); - }; - xhr.onerror = function () { - return reject(errorMessages.AUTH_ENDPOINT_NETWORK_ERROR); - } - xhr.onload = function () { - if (xhr.status === 200) { - try { - var body = JSON.parse(xhr.responseText); - var obj = { - signature: body.signature, - expire: body.expire, - token: body.token - } - if (!obj.signature || !obj.expire || !obj.token) { - return reject(errorMessages.AUTH_INVALID_RESPONSE); - } - return resolve(obj); - } catch (ex) { - return reject(errorMessages.AUTH_INVALID_RESPONSE); - } - } else { - return reject(errorMessages.AUTH_INVALID_RESPONSE); - } - }; - xhr.send(); - }); -} - export const uploadFile = ( uploadFileXHR: XMLHttpRequest, formData: FormData diff --git a/test/data/index.js b/test/data/index.js index 5e0ede3..d7676c2 100644 --- a/test/data/index.js +++ b/test/data/index.js @@ -1,5 +1,4 @@ module.exports.initializationParams = { publicKey: "test_public_key", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - authenticationEndpoint: "http://test/auth" } \ No newline at end of file diff --git a/test/initialization.js b/test/initialization.js index 8f87592..b3695ed 100644 --- a/test/initialization.js +++ b/test/initialization.js @@ -41,7 +41,6 @@ describe("Initialization checks", function () { 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 () { From 54886d095dac2a5ac1269289f4436090da26822f Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Thu, 20 Jul 2023 14:50:33 +0530 Subject: [PATCH 004/166] feat: add missing security parameters test case and modify related test cases - Added a new test case to cover missing security parameters during file upload. - Updated existing test cases to reflect changes after removing authentication endpoint and related logic. --- src/constants/errorMessages.ts | 1 + src/upload/index.ts | 20 ++- test/upload.js | 239 ++++++++------------------------- 3 files changed, 73 insertions(+), 187 deletions(-) diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 0787193..f39ca3b 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -15,4 +15,5 @@ export default { help: "", }, INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, + MISSING_SECURITY_PARAMETERS: { message: "Missing security paramters for upload", help: ""} }; diff --git a/src/upload/index.ts b/src/upload/index.ts index 2aa13d2..90961a3 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -23,6 +23,12 @@ export const upload = ( respond(true, errorMessages.MISSING_PUBLIC_KEY, callback); return; } + + if(!uploadOptions.token || !uploadOptions.signature || !uploadOptions.expire) { + respond(true, errorMessages.MISSING_SECURITY_PARAMETERS, callback) + return + } + var formData = new FormData(); let key: keyof typeof uploadOptions; for (key in uploadOptions) { @@ -30,7 +36,13 @@ export const upload = ( if (key === "file" && typeof uploadOptions.file != "string") { formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { - formData.append('tags', uploadOptions.tags.join(",")); + formData.append('tags', uploadOptions.tags.join(",")); + } else if (key === 'signature') { + formData.append("signature", uploadOptions.signature); + } else if (key === 'expire') { + formData.append("expire", String(uploadOptions.expire)); + } else if (key === 'token') { + formData.append("token", uploadOptions.token); } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { formData.append('responseFields', uploadOptions.responseFields.join(",")); } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { @@ -40,12 +52,6 @@ export const upload = ( formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); } else if(uploadOptions[key] !== undefined) { formData.append(key, String(uploadOptions[key])); - } else if (key === 'signature') { - formData.append("signature", uploadOptions.signature); - } else if (key === 'expire') { - formData.append("expire", String(uploadOptions.expire)); - } else if (key === 'token') { - formData.append("token", uploadOptions.token); } } } diff --git a/test/upload.js b/test/upload.js index 24cded2..c6b2333 100644 --- a/test/upload.js +++ b/test/upload.js @@ -26,30 +26,10 @@ const uploadSuccessResponseObj = { "extensionStatus": { "aws-auto-tagging": "success" } }; -function successSignature() { - server.respondWith("GET", new RegExp(initializationParams.authenticationEndpoint + ".*"), - [ - 200, - { "Content-Type": "application/json" }, - JSON.stringify({ - signature: "test_signature", - expire: 123, - token: "test_token" - }) - ]); - server.respond(); -} - -function nonSuccessErrorSignature() { - server.respondWith("GET", new RegExp(initializationParams.authenticationEndpoint + ".*"), - [ - 403, - { "Content-Type": "application/json" }, - JSON.stringify({ - error: "Not allowed" - }) - ]); - server.respond(); +const securityParameters = { + signature: "test_signature", + expire: 123, + token: "test_token" } function successUploadResponse() { @@ -114,6 +94,7 @@ describe("File upload", function () { it('Missing fileName', function () { const fileOptions = { + ...securityParameters, file: "https://ik.imagekit.io/remote-url.jpg" }; @@ -127,6 +108,7 @@ describe("File upload", function () { it('Missing file', function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", }; @@ -138,7 +120,7 @@ describe("File upload", function () { sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); }); - it('Missing authEndpoint', function () { + it('Missing security parameters', function () { const fileOptions = { fileName: "test_file_name", file: "test_file" @@ -146,12 +128,10 @@ describe("File upload", function () { var callback = sinon.spy(); - imagekit.upload(fileOptions, callback, { - authenticationEndpoint: "" - }); - + imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); - sinon.assert.calledWith(callback, { message: "Missing authentication endpoint for upload", help: "" }, null); + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, { message: "Missing security paramters for upload", help: "" }, null); }); it('Missing public key', function () { @@ -170,97 +150,9 @@ describe("File upload", function () { sinon.assert.calledWith(callback, { message: "Missing public key for upload", help: "" }, null); }); - it('Auth endpoint network error handling', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback, { - authenticationEndpoint: "https://does-not-exist-sdfsdf/aut" - }); - - expect(server.requests.length).to.be.equal(2); - - // Simulate network error on authentication endpoint - server.requests[1].error(); - await sleep(); - sinon.assert.calledWith(callback, { message: "Request to authenticationEndpoint failed due to network error", help: "" }, null); - }); - - it('Auth endpoint non 200 status code handling', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(2); - - // Simulate non 200 response on authentication endpoint - nonSuccessErrorSignature(); - await sleep(); - sinon.assert.calledWith(callback, { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, null); - }); - - it('Auth endpoint 200 status with bad body', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(2); - - // Simulate non 200 response on authentication endpoint - server.respondWith("GET", new RegExp(initializationParams.authenticationEndpoint + ".*"), - [ - 200, - { "Content-Type": "application/json" }, - "invalid json" - ]); - server.respond(); - await sleep(); - sinon.assert.calledWith(callback, { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, null); - }); - - it('Auth endpoint 200 status missing token', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file" - }; - - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(2); - - // Simulate non 200 response on authentication endpoint - server.respondWith("GET", new RegExp(initializationParams.authenticationEndpoint + ".*"), - [ - 200, - { "Content-Type": "application/json" }, - JSON.stringify({ - signature: "sig", - timestamp: "123" - }) - ]); - server.respond(); - await sleep(); - sinon.assert.calledWith(callback, { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, null); - }); - it('Upload endpoint network error handling', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file" }; @@ -269,8 +161,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); // Simulate network error on upload API @@ -281,6 +172,7 @@ describe("File upload", function () { it('Boolean handling', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -294,8 +186,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -319,6 +210,7 @@ describe("File upload", function () { it('Tag array handling', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: ["test_tag1", "test_tag2"], @@ -330,8 +222,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -353,6 +244,7 @@ describe("File upload", function () { it('Missing useUniqueFileName', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: ["test_tag1", "test_tag2"], @@ -363,8 +255,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -388,6 +279,7 @@ describe("File upload", function () { it('Missing isPrivateFile', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: ["test_tag1", "test_tag2"] @@ -397,8 +289,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -422,6 +313,7 @@ describe("File upload", function () { it('With extensions parameter', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -442,8 +334,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -470,6 +361,7 @@ describe("File upload", function () { it('Bare minimum request', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: undefined @@ -479,8 +371,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -505,6 +396,7 @@ describe("File upload", function () { it('Bare minimum request: Blob', async function () { const buffer = Buffer.from("test_buffer") const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: buffer }; @@ -513,8 +405,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -539,6 +430,7 @@ describe("File upload", function () { it('Error during upload', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file" }; @@ -547,8 +439,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { help: "For support kindly contact us at support@imagekit.io .", @@ -562,6 +453,7 @@ describe("File upload", function () { it('Error during upload non 2xx with bad body', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file" }; @@ -570,8 +462,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", [ @@ -589,6 +480,7 @@ describe("File upload", function () { it('Error during upload 2xx with bad body', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file" }; @@ -597,8 +489,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", [ @@ -616,6 +507,7 @@ describe("File upload", function () { it('Upload via URL', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "https://ik.imagekit.io/remote-url.jpg" }; @@ -624,8 +516,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -647,11 +538,11 @@ describe("File upload", function () { sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); - it('Overriding public key and authentication endpoint', async function () { - var newAuthEndpoint = "http://test/auth-override"; + it('Overriding public', async function () { var newPublicKey = "override_public_key"; const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "https://ik.imagekit.io/remote-url.jpg" }; @@ -659,22 +550,10 @@ describe("File upload", function () { var callback = sinon.spy(); imagekit.upload(fileOptions, callback, { - authenticationEndpoint: newAuthEndpoint, publicKey: newPublicKey }); - expect(server.requests.length).to.be.equal(2); - server.respondWith("GET", new RegExp(newAuthEndpoint + ".*"), - [ - 200, - { "Content-Type": "application/json" }, - JSON.stringify({ - signature: "override_test_signature", - expire: 123123, - token: "override_test_token" - }) - ]); - server.respond(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -682,9 +561,9 @@ describe("File upload", function () { var arg = server.requests[0].requestBody; expect(arg.get('file')).to.be.equal("https://ik.imagekit.io/remote-url.jpg"); expect(arg.get('fileName')).to.be.equal("test_file_name"); - expect(arg.get('token')).to.be.equal("override_test_token"); - expect(arg.get('expire')).to.be.equal("123123"); - expect(arg.get('signature')).to.be.equal("override_test_signature"); + expect(arg.get('token')).to.be.equal("test_token"); + expect(arg.get('expire')).to.be.equal("123"); + expect(arg.get('signature')).to.be.equal("test_signature"); expect(arg.get('publicKey')).to.be.equal('override_public_key'); expect(arg.get('tags')).to.be.equal(undefined); expect(arg.get('isPrivateFile')).to.be.equal(undefined); @@ -700,6 +579,7 @@ describe("File upload", function () { it('With overwrite parameters', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -723,8 +603,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -754,6 +633,7 @@ describe("File upload", function () { it('With customMetadata', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -781,8 +661,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -813,6 +692,7 @@ describe("File upload", function () { it('Array type fields', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: ["test_tag1", "test_tag2"], @@ -840,8 +720,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); @@ -875,6 +754,7 @@ describe("File upload", function () { var fun = function () { return "hello from function" }; xhr.onprogress = fun; const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -893,10 +773,9 @@ describe("File upload", function () { }; var callback = sinon.spy(); imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); + expect(server.requests.length).to.be.equal(1); expect(server.requests[0]).to.be.equal(xhr); expect(server.requests[0].onprogress.toString()).to.be.equal(fun.toString()); - successSignature(); await sleep(); successUploadResponse(); await sleep(); @@ -922,6 +801,7 @@ describe("File upload", function () { it('Upload using promise - success', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -939,9 +819,8 @@ describe("File upload", function () { }; var uploadPromise = imagekit.upload(fileOptions); - expect(server.requests.length).to.be.equal(2); + expect(server.requests.length).to.be.equal(1); - successSignature(); await sleep(); successUploadResponse(); await sleep(); @@ -970,6 +849,7 @@ describe("File upload", function () { message: "Your account cannot be authenticated." } const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -988,7 +868,6 @@ describe("File upload", function () { try { var uploadPromise = imagekit.upload(fileOptions); - successSignature(); await sleep(); errorUploadResponse(500, errRes); await sleep(); @@ -1003,6 +882,7 @@ describe("File upload", function () { var fun = function () { return "hello from function" }; xhr.onprogress = fun; const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -1021,10 +901,9 @@ describe("File upload", function () { }; var uploadPromise = imagekit.upload(fileOptions); - expect(server.requests.length).to.be.equal(2); + expect(server.requests.length).to.be.equal(1); - successSignature(); await sleep(); successUploadResponse(); await sleep(); @@ -1057,6 +936,7 @@ describe("File upload", function () { "x-request-id": "sdfsdfsdfdsf" }; const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: "test_tag1,test_tag2", @@ -1074,9 +954,8 @@ describe("File upload", function () { }; var uploadPromise = imagekit.upload(fileOptions) - expect(server.requests.length).to.be.equal(2); + expect(server.requests.length).to.be.equal(1); - successSignature(); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -1100,15 +979,15 @@ describe("File upload", function () { "x-request-id": "sdfsdfsdfdsf" }; const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file" }; var callback = sinon.spy(); imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); + expect(server.requests.length).to.be.equal(1); - successSignature(); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", [ @@ -1133,6 +1012,7 @@ describe("File upload", function () { it('Undefined fields should not be sent', async function () { const fileOptions = { + ...securityParameters, fileName: "test_file_name", file: "test_file", tags: undefined, @@ -1153,8 +1033,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); - expect(server.requests.length).to.be.equal(2); - successSignature(); + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); From a729345a9f0651f9256318d08bf81a8486ee3953 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Fri, 21 Jul 2023 11:05:12 +0530 Subject: [PATCH 005/166] fix: textual changes --- src/constants/errorMessages.ts | 2 +- src/interfaces/UploadOptions.ts | 2 +- test/upload.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index f39ca3b..19253c3 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -15,5 +15,5 @@ export default { help: "", }, INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, - MISSING_SECURITY_PARAMETERS: { message: "Missing security paramters for upload", help: ""} + MISSING_SECURITY_PARAMETERS: { message: "Missing security parameters for upload. The SDK expects token, signature and expire for authentication.", help: ""} }; diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 3e478e9..8ac7a3a 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -14,7 +14,7 @@ export interface UploadOptions { */ file: string | Blob | File; /** - * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. Learn how to create a signature below on the page. This should be in lowercase. + * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. This should be in lowercase. * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. */ signature: string; diff --git a/test/upload.js b/test/upload.js index c6b2333..b57c3a2 100644 --- a/test/upload.js +++ b/test/upload.js @@ -131,7 +131,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing security paramters for upload", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing security parameters for upload. The SDK expects token, signature and expire authentication.", help: "" }, null); }); it('Missing public key', function () { From 4d98e1ce6b91e548118d937338b6f9a9a60a51b4 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Fri, 21 Jul 2023 14:15:27 +0530 Subject: [PATCH 006/166] fix: message type in upload test cases --- test/upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/upload.js b/test/upload.js index b57c3a2..eb8a0e8 100644 --- a/test/upload.js +++ b/test/upload.js @@ -131,7 +131,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing security parameters for upload. The SDK expects token, signature and expire authentication.", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing security parameters for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); }); it('Missing public key', function () { From f1e35efba606a4564eab57e69f0d955d553fa368 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Fri, 21 Jul 2023 14:21:20 +0530 Subject: [PATCH 007/166] fix: demo project changes --- samples/sample-app/server/server.js | 18 ++++-------------- samples/sample-app/views/index.pug | 7 ++++++- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/samples/sample-app/server/server.js b/samples/sample-app/server/server.js index b0a5982..628fbf4 100644 --- a/samples/sample-app/server/server.js +++ b/samples/sample-app/server/server.js @@ -21,25 +21,15 @@ const startServer = (port = 3000, PUBLIC_KEY, PRIVATE_KEY, URL_ENDPOINT) => { urlEndpoint: URL_ENDPOINT }); - - router.get("/auth", (req, res) => { + router.get("/", (req, res) => { try { + // Generating security parameters. + // For generating token, signature and expire again just refresh the page. const token = req.query.token || uuid.v4(); const expiration = req.query.expire || parseInt(Date.now()/1000)+ (60 * 10); // Default expiration in 10 mins - const signatureObj = imagekit.getAuthenticationParameters(token, expiration); - - res.status(200).send(signatureObj); - - } catch (err) { - console.error("Error while responding to auth request:", JSON.stringify(err, undefined, 2)); - res.status(500).send("Internal Server Error"); - } - }); - router.get("/", (req, res) => { - try { - res.render(pugTemplatePath, {publicKey: PUBLIC_KEY, urlEndpoint: URL_ENDPOINT, authenticationEndpoint: `http://localhost:3000/auth`}); + res.render(pugTemplatePath, {publicKey: PUBLIC_KEY, urlEndpoint: URL_ENDPOINT, signatureObj}); } catch (err) { console.error("Error while responding to static page request:", JSON.stringify(err, undefined, 2)); res.status(500).send("Internal Server Error"); diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug index b27d3e9..cba0be3 100644 --- a/samples/sample-app/views/index.pug +++ b/samples/sample-app/views/index.pug @@ -24,7 +24,6 @@ html var imagekit = new ImageKit({ publicKey: "!{publicKey}", urlEndpoint: "!{urlEndpoint}", - authenticationEndpoint: "!{authenticationEndpoint}" }); window.imagekit = imagekit; @@ -55,6 +54,12 @@ html file : file.files[0], fileName : file.files[0].name || "test_image.jpg", tags : ["test_tag_1"], + token: "!{signatureObj.token}", + signature: "!{signatureObj.signature}", + expire: "!{signatureObj.expire}" + //- token: "63b76592-e9be-428b-8fc7-d4e15aba7506", + //- expire: 1689917695, + //- signature: "7ddcf61b7e54036c88ba838a9bc86fa969eb1f5b" //- extensions: [ //- { //- name: "aws-auto-tagging", From 97053da54995a6ac3f69cac3f707588d38d233b2 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Fri, 21 Jul 2023 15:55:15 +0530 Subject: [PATCH 008/166] fix: test case typo --- test/upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/upload.js b/test/upload.js index eb8a0e8..117f4f1 100644 --- a/test/upload.js +++ b/test/upload.js @@ -538,7 +538,7 @@ describe("File upload", function () { sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); - it('Overriding public', async function () { + it('Overriding public key', async function () { var newPublicKey = "override_public_key"; const fileOptions = { From 19143607461fe88c055f517a35bd15b49ff00e74 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Tue, 25 Jul 2023 10:47:43 +0530 Subject: [PATCH 009/166] fix: add individual null checks for security paramters --- src/constants/errorMessages.ts | 4 +++- src/upload/index.ts | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 19253c3..ad7ada6 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -15,5 +15,7 @@ export default { help: "", }, INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, - MISSING_SECURITY_PARAMETERS: { message: "Missing security parameters for upload. The SDK expects token, signature and expire for authentication.", help: ""} + MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects signature for authentication.", help: ""}, + MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token for authentication.", help: ""}, + MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects expire for authentication.", help: ""}, }; diff --git a/src/upload/index.ts b/src/upload/index.ts index 90961a3..dd705bc 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -24,8 +24,18 @@ export const upload = ( return; } - if(!uploadOptions.token || !uploadOptions.signature || !uploadOptions.expire) { - respond(true, errorMessages.MISSING_SECURITY_PARAMETERS, callback) + if(!uploadOptions.token) { + respond(true, errorMessages.MISSING_TOKEN, callback) + return + } + + if(!uploadOptions.signature) { + respond(true, errorMessages.MISSING_SIGNATURE, callback) + return + } + + if(!uploadOptions.expire) { + respond(true, errorMessages.MISSING_EXPIRE, callback) return } From 527e6332c8473426339055c8fd248cfe858f48d7 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Tue, 25 Jul 2023 11:02:08 +0530 Subject: [PATCH 010/166] fix: individual test cases for security params --- test/upload.js | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/test/upload.js b/test/upload.js index 117f4f1..a76d5e9 100644 --- a/test/upload.js +++ b/test/upload.js @@ -119,11 +119,45 @@ describe("File upload", function () { expect(callback.calledOnce).to.be.true; sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); }); + + it('Missing token', function () { + const fileOptions = { + fileName: "test_file_name", + file: "test_file", + signature: 'test_signature', + expire: 123 + }; - it('Missing security parameters', function () { + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + expect(server.requests.length).to.be.equal(1); + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token for authentication.", help: "" }, null); + }); + + it('Missing signature', function () { const fileOptions = { fileName: "test_file_name", - file: "test_file" + file: "test_file", + token: 'test_token', + expire: 123 + }; + + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + expect(server.requests.length).to.be.equal(1); + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects signature for authentication.", help: "" }, null); + }); + + it('Missing expire', function () { + const fileOptions = { + fileName: "test_file_name", + file: "test_file", + token: 'test_token', + signature: 'test_signature' }; var callback = sinon.spy(); @@ -131,7 +165,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing security parameters for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects expire for authentication.", help: "" }, null); }); it('Missing public key', function () { From f4b0628dea3c4429ce1b5a078edd3d0fa0fd9911 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Tue, 25 Jul 2023 11:11:19 +0530 Subject: [PATCH 011/166] fix: security parameters error text --- src/constants/errorMessages.ts | 6 +++--- test/upload.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index ad7ada6..b8e328a 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -15,7 +15,7 @@ export default { help: "", }, INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, - MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects signature for authentication.", help: ""}, - MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token for authentication.", help: ""}, - MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects expire for authentication.", help: ""}, + MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, sginature and expire for authentication.", help: ""}, + MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, sginature and expire for authentication.", help: ""}, + MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, sginature and expire for authentication.", help: ""}, }; diff --git a/test/upload.js b/test/upload.js index a76d5e9..1da2c02 100644 --- a/test/upload.js +++ b/test/upload.js @@ -133,7 +133,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token for authentication.", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token, sginature and expire for authentication.", help: "" }, null); }); it('Missing signature', function () { @@ -149,7 +149,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects signature for authentication.", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects token, sginature and expire for authentication.", help: "" }, null); }); it('Missing expire', function () { @@ -165,7 +165,7 @@ describe("File upload", function () { imagekit.upload(fileOptions, callback); expect(server.requests.length).to.be.equal(1); expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects expire for authentication.", help: "" }, null); + sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects token, sginature and expire for authentication.", help: "" }, null); }); it('Missing public key', function () { From 461828de69e418be954b5cefac1aafe206c5d206 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Tue, 25 Jul 2023 14:08:08 +0530 Subject: [PATCH 012/166] fix: add auth endpoint and api call --- samples/sample-app/server/server.js | 18 +++-- samples/sample-app/views/index.pug | 100 ++++++++++++++++++---------- 2 files changed, 76 insertions(+), 42 deletions(-) diff --git a/samples/sample-app/server/server.js b/samples/sample-app/server/server.js index 628fbf4..5942f56 100644 --- a/samples/sample-app/server/server.js +++ b/samples/sample-app/server/server.js @@ -3,7 +3,6 @@ const router = express.Router(); const cors = require('cors'); const ImageKit = require('imagekit'); const uuid = require('uuid'); -const fs = require('fs'); const path = require('path'); const pugTemplatePath = path.join(__dirname, "../views/index.pug"); @@ -21,15 +20,24 @@ const startServer = (port = 3000, PUBLIC_KEY, PRIVATE_KEY, URL_ENDPOINT) => { urlEndpoint: URL_ENDPOINT }); - router.get("/", (req, res) => { + router.get("/auth", (req, res) => { try { - // Generating security parameters. - // For generating token, signature and expire again just refresh the page. const token = req.query.token || uuid.v4(); const expiration = req.query.expire || parseInt(Date.now()/1000)+ (60 * 10); // Default expiration in 10 mins + const signatureObj = imagekit.getAuthenticationParameters(token, expiration); + + res.status(200).send(signatureObj); + + } catch (err) { + console.error("Error while responding to auth request:", JSON.stringify(err, undefined, 2)); + res.status(500).send("Internal Server Error"); + } + }); - res.render(pugTemplatePath, {publicKey: PUBLIC_KEY, urlEndpoint: URL_ENDPOINT, signatureObj}); + router.get("/", (req, res) => { + try { + res.render(pugTemplatePath, {publicKey: PUBLIC_KEY, urlEndpoint: URL_ENDPOINT, authenticationEndpoint: `http://localhost:3000/auth`}); } catch (err) { console.error("Error while responding to static page request:", JSON.stringify(err, undefined, 2)); res.status(500).send("Internal Server Error"); diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug index cba0be3..6f0b44f 100644 --- a/samples/sample-app/views/index.pug +++ b/samples/sample-app/views/index.pug @@ -33,6 +33,7 @@ html e.preventDefault(); var file = document.getElementById("file1"); var fileSize = file.files[0].size; + var AUTH_INVALID_RESPONSE = "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire." var statusEl = document.getElementById("status"); statusEl.innerHTML = "Uploading..."; @@ -49,47 +50,72 @@ html } }); - imagekit.upload({ - xhr: customXHR, // Use this if you want to track upload progress - file : file.files[0], - fileName : file.files[0].name || "test_image.jpg", - tags : ["test_tag_1"], - token: "!{signatureObj.token}", - signature: "!{signatureObj.signature}", - expire: "!{signatureObj.expire}" - //- token: "63b76592-e9be-428b-8fc7-d4e15aba7506", - //- expire: 1689917695, - //- signature: "7ddcf61b7e54036c88ba838a9bc86fa969eb1f5b" - //- extensions: [ - //- { - //- name: "aws-auto-tagging", - //- minConfidence: 80, - //- maxTags: 10 - //- } - //- ], - }, function(err, result) { - if (err) { - statusEl.innerHTML = "Error uploading image. "+ err.message; - console.log(err) - } else { - statusEl.innerHTML = "File Uploaded"; - var sampleTransformations = [{ HEIGHT: 300, WIDTH: 400}]; - srcUrl = result.url; - transformedURL = imagekit.url({ - src: srcUrl, - transformation : sampleTransformations - }); + // Generating security parameters using authenticationEndpoint + const securityParametersRequest = new XMLHttpRequest(); + securityParametersRequest.timeout = 60000; + var urlObj = new URL("!{authenticationEndpoint}"); + securityParametersRequest.open('GET', urlObj.toString()); + securityParametersRequest.ontimeout = function (e) { + console.log(e.message); + return statusEl.innerHTML = "Timeout generating security parameters. "+ e.message + }; + securityParametersRequest.onerror = function (e) { + console.log(e.message) + return statusEl.innerHTML = "Request to authenticationEndpoint failed due to network error."+ e.message + } + securityParametersRequest.onload = () => { + if(securityParametersRequest.status === 200) { + var securityParametersObj = JSON.parse(securityParametersRequest.response) + + if(!securityParametersObj || !securityParametersObj.token || !securityParametersObj.signature || !securityParametersObj.expire) { + return statusEl.innerHTML = AUTH_INVALID_RESPONSE; + } + + // Uploading image + imagekit.upload({ + xhr: customXHR, // Use this if you want to track upload progress + file : file.files[0], + fileName : file.files[0].name || "test_image.jpg", + tags : ["test_tag_1"], + token: securityParametersObj.token, + signature: securityParametersObj.signature, + expire: securityParametersObj.expire, + //- extensions: [ + //- { + //- name: "aws-auto-tagging", + //- minConfidence: 80, + //- maxTags: 10 + //- } + //- ], + }, function(err, result) { + if (err) { + statusEl.innerHTML = "Error uploading image. "+ err.message; + console.log(err) + } else { + statusEl.innerHTML = "File Uploaded"; + var sampleTransformations = [{ HEIGHT: 300, WIDTH: 400}]; + srcUrl = result.url; + transformedURL = imagekit.url({ + src: srcUrl, + transformation : sampleTransformations + }); - var orig_img = document.querySelector("#orig_image > p > img"); - var trans_img = document.querySelector("#trans_image > p > img"); + var orig_img = document.querySelector("#orig_image > p > img"); + var trans_img = document.querySelector("#trans_image > p > img"); - orig_img.setAttribute("src", srcUrl); - trans_img.setAttribute("src", transformedURL); + orig_img.setAttribute("src", srcUrl); + trans_img.setAttribute("src", transformedURL); - var el = document.getElementById('images') - el.setAttribute("style", ""); + var el = document.getElementById('images') + el.setAttribute("style", ""); + } + }); + } else { + return statusEl.innerHTML = AUTH_INVALID_RESPONSE; } - }); + } + + securityParametersRequest.send(); } } catch(ex) { console.log(ex); From f0ed0f424ec6a11807f57bbc18b1aaa0a3aa3c32 Mon Sep 17 00:00:00 2001 From: Prasoon Shukla Date: Mon, 31 Jul 2023 12:15:03 +0530 Subject: [PATCH 013/166] feat: update readme --- README.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ebe0205..653cb45 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ Javascript SDK for [ImageKit](https://imagekit.io/) provides URL generation for 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. +## Changelog - SDK Version 2.0.0 +### Breaking changes +**1. Authentication Process Update:** +* Previously, when using client side file upload, the SDK required the `publicKey` and `authenticationEndpoint` parameters during SDK initialization to fetch security parameters (`signature`, `token`, and `expire`). +* In version 2.0.0, we've updated the authentication process for the SDK. As a user of the SDK, you are now responsible for generating the security parameters (`signature`, `token`, and `expire`) yourself. This means you no longer need to provide the `authenticationEndpoint`. When using the SDK's upload method, make sure to pass these security parameters explicitly along with other [upload options](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). For guidance on generating these security parameters, please refer to the documentation available [here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). + ## Installation ### Using npm @@ -59,12 +65,11 @@ var imagekit = new ImageKit({ }); ``` -`publicKey` and `authenticationEndpoint` parameters are required if you want to use the SDK for client-side file upload. You can get these parameters from the developer section in your ImageKit dashboard - https://imagekit.io/dashboard#developers +`publicKey` parameter is required if you want to use the SDK for client-side file upload. You can get this parameter from the developer section in your ImageKit dashboard - https://imagekit.io/dashboard#developers ``` var imagekit = new ImageKit({ publicKey: "your_public_api_key", urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", - authenticationEndpoint: "http://www.yourserver.com/auth", }); ``` @@ -261,9 +266,7 @@ The SDK provides a simple interface using the `.upload()` method to upload files The `upload()` method requires mandatory `file` and the `fileName` parameter. In addition, it accepts all the parameters supported by the [ImageKit Upload API](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). -Also, ensure that you have specified `authenticationEndpoint` during SDK initialization. The SDK makes an HTTP GET request to this endpoint and expects a JSON response with three fields, i.e. `signature`, `token`, and `expire`. In addition, the SDK adds a query parameter `t` with a random value to ensure that the request URL is unique and the response is not cached in [Safari iOS](https://github.com/imagekit-developer/imagekit-javascript/issues/59). Your backend can ignore this query parameter. - -[Learn how to implement authenticationEndpoint](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload#how-to-implement-authenticationendpoint-endpoint) on your server. +Also before making an upload request, please ensure you have generated mandatory security parameters: `signature`, `token`, and `expire`. To generate these security parameters, refer to the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). Obtain the parameters using a secure method and pass them, along with the mandatory `file` and `fileName` parameters, to the `upload()` method. 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 specify tags for a file at the time of upload, use the `tags` parameter as specified in the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). @@ -277,13 +280,12 @@ You can pass other parameters supported by the ImageKit upload API using the sam ``` ## Initialization -`urlEndpoint` is required to use the SDK. You can get URL-endpoint from your ImageKit dashboard - https://imagekit.io/dashboard#url-endpoints - -``` +Initialize the SDK with your URL endpoint. For URL generation: +```js var imagekit = new ImageKit({ urlEndpoint: "https://ik.imagekit.io/your_imagekit_id" -}); -``` - -`publicKey` parameter is required if you want to use the SDK for client-side file upload. You can get this parameter from the developer section in your ImageKit dashboard - https://imagekit.io/dashboard#developers +}); ``` +For client-side file uploads, include your public key: +```js var imagekit = new ImageKit({ publicKey: "your_public_api_key", urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", -}); -``` - -*Note: Do not include your Private Key in any client-side code, including this SDK or its initialization. If you pass the `privateKey` parameter while initializing this SDK, it throws an error* - -## Demo Application - -The fastest way to get started is by running the demo application in [samples/sample-app](https://github.com/imagekit-developer/imagekit-javascript/tree/master/samples/sample-app) folder. Follow these steps to run the application locally: - -``` -git clone https://github.com/imagekit-developer/imagekit-javascript.git - -cd imagekit-javascript -``` - -Create a file `.env` using `sample.env` in the directory `samples/sample-app` and fill in your `PRIVATE_KEY`, `PUBLIC_KEY` and `URL_ENDPOINT` from your [imageKit dashboard](https://imagekit.io/dashboard#developers). `SERVER_PORT` must also be included as per the `sample.env` file. - -Now start the sample application by running: - -``` -// Run it from the project root -yarn startSampleApp -``` - -## Usage -You can use this SDK for URL generation and client-side file uploads. - -### URL Generation - -**1. Using image path and image hostname or endpoint** - -This method allows you to create an URL to access a file using the relative file path and the ImageKit URL endpoint (`urlEndpoint`). The file can be an image, video, or any other static file supported by ImageKit. - -``` -var imageURL = imagekit.url({ - path: "/default-image.jpg", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation: [{ - "height": "300", - "width": "400" - }] -}); -``` - -This results in a URL like - -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/tr:h-300,w-400/default-image.jpg -``` - -**2. Using full image URL** - -This method allows you to add transformation parameters to an absolute URL. For example, if you have configured a custom CNAME and have absolute asset URLs in your database or CMS, you will often need this. - - -``` -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] }); ``` +*Note: Never include your private key in client-side code. If provided, the SDK throws an error.* -This results in a URL like - -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400 -``` +## URL Generation +The SDK’s `.url()` method enables you to generate optimized image and video URLs with a variety of transformations. -The `.url()` method accepts the following parameters +### Basic URL Generation -| 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. | -| transformationPostion | Optional. The default value is `path`, which 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 query parameter `tr` in the URL. 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 are not necessarily related to ImageKit. Especially useful if you want to add some versioning parameters to your URLs. | +1. **Using an Image Path with a URL Endpoint** + ```js + var imageURL = imagekit.url({ + path: "/default-image.jpg", + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", + transformation: [{ + "height": "300", + "width": "400" + }] + }); + ``` + *Result Example:* + ``` + https://ik.imagekit.io/your_imagekit_id/endpoint/tr:h-300,w-400/default-image.jpg + ``` + +2. **Using a Full Image URL (src)** + ```js + var imageURL = imagekit.url({ + src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", + transformation: [{ + "height": "300", + "width": "400" + }] + }); + ``` + *Result Example:* + ``` + https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400 + ``` -#### Examples of generating URLs +### Advanced URL Generation Examples -**1. Chained Transformations as a query parameter** -``` +#### Chained Transformations +Apply multiple transformations by passing an array: +```js var imageURL = imagekit.url({ path: "/default-image.jpg", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", transformation: [{ "height": "300", "width": "400" }, { "rotation": 90 }], - transformationPosition: "query" -}); -``` -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400%3Art-90 -``` - -**2. Sharpening and contrast transforms and a progressive JPG image** - -There are some transforms like [Sharpening](https://docs.imagekit.io/features/image-transformations/image-enhancement-and-color-manipulation) that can be added to the URL with or without any other value. To use such transforms without specifying a value, specify the value as "-" in the transformation object. Otherwise, specify the value that you want to be added to this transformation. - -``` -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation: [{ - "format": "jpg", - "progressive": "true", - "effectSharpen": "-", - "effectContrast": "1" - }] + transformationPosition: "query" // Use query parameter for transformations }); ``` +*Result Example:* ``` -//Note that because `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 +https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300%2Cw-400%3Art-90 ``` -**3. Adding overlays** - -ImageKit.io enables you to apply overlays to [images](https://docs.imagekit.io/features/image-transformations/overlay-using-layers) and [videos](https://docs.imagekit.io/features/video-transformation/overlay) using the raw parameter with the concept of [layers](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#layers). The raw parameter facilitates incorporating transformations directly in the URL. A layer is a distinct type of transformation that allows you to define an asset to serve as an overlay, along with its positioning and additional transformations. - -**Text as overlays** - -You can add any text string over a base video or image using a text layer (l-text). - -For example: - +#### Overlays and Effects +*Text Overlay Example:* ```js var imageURL = imagekit.url({ src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", transformation: [{ "width": 400, - "height": 300 + "height": 300, "raw": "l-text,i-Imagekit,fs-50,l-end" }] }); ``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-text,i-Imagekit,fs-50,l-end/default-image.jpg -``` - -**Image as overlays** - -You can add an image over a base video or image using an image layer (l-image). - -For example: - +*Image Overlay Example:* ```js var imageURL = imagekit.url({ src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", transformation: [{ "width": 400, - "height": 300 + "height": 300, "raw": "l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end" }] }); ``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end/default-image.jpg -``` - -**Solid color blocks as overlays** - -You can add solid color blocks over a base video or image using an image layer (l-image). - -For example: - -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/img/sample-video.mp4", - transformation: [{ - "width": 400, - "height": 300 - "raw": "l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end" - }] -}); -``` -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/tr:h-300,w-400,l-image,i-ik_canvas,bg-FF0000,w-300,h-100,l-end/img/sample-video.mp4 -``` - -**4. Arithmetic expressions in transformations** - -ImageKit allows use of [arithmetic expressions](https://docs.imagekit.io/features/arithmetic-expressions-in-transformations) in certain dimension and position-related parameters, making media transformations more flexible and dynamic. - -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" - }] -}); -``` - -**Sample Result URL** -``` -https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=w-iw_div_4,h-ih_div_2,b-cw_mul_0.05_yellow -``` - -### Key Advanced Transformations - -#### Background Removal +#### AI and Advanced Transformations +*Background Removal:* ```js var imageURL = imagekit.url({ path: "/sample-image.jpg", @@ -297,12 +167,8 @@ var imageURL = imagekit.url({ aiRemoveBackground: true }] }); -console.log("Background Removed Image URL:", imageURL); -// Expected: https://ik.imagekit.io/your_imagekit_id/tr:e-bgremove/sample-image.jpg ``` - -#### Upscaling - +*Upscaling:* ```js var upscaledURL = imagekit.url({ path: "/sample-image.jpg", @@ -310,12 +176,8 @@ var upscaledURL = imagekit.url({ aiUpscale: true }] }); -console.log("Upscaled Image URL:", upscaledURL); -// Expected: https://ik.imagekit.io/your_imagekit_id/tr:e-upscale/sample-image.jpg ``` - -#### AI Drop Shadow - +*Drop Shadow:* ```js var dropShadowURL = imagekit.url({ path: "/sample-image.jpg", @@ -323,250 +185,156 @@ var dropShadowURL = imagekit.url({ aiDropShadow: "az-45" }] }); -console.log("Image URL with Drop Shadow:", dropShadowURL); -// Expected: https://ik.imagekit.io/your_imagekit_id/tr:e-dropshadow-az-45/sample-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 | -| ----------------------------- | ------------------------------------------------------------- | -| width | w | -| height | h | -| aspectRatio | ar | -| quality | q | -| aiRemoveBackground | e-bgremove (ImageKit powered) | -| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | -| aiUpscale | e-upscale | -| aiRetouch | e-retouch | -| aiVariation | e-genvar | -| aiDropShadow | e-dropshadow | -| aiChangeBackground | e-changebg | -| crop | c | -| cropMode | cm | -| x | x | -| y | y | -| xCenter | xc | -| yCenter | yc | -| focus | fo | -| format | f | -| radius | r | -| background | bg | -| border | b | -| rotation | rt | -| blur | bl | -| named | n | -| dpr | dpr | -| progressive | pr | -| lossless | lo | -| trim | t | -| metadata | md | -| colorProfile | cp | -| defaultImage | di | -| original | orig | -| videoCodec | vc | -| audioCodec | ac | -| grayscale | e-grayscale | -| contrastStretch | e-contrast | -| shadow | e-shadow | -| sharpen | e-sharpen | -| unsharpMask | e-usm | -| gradient | e-gradient | -| opacity | o | -| zoom | z | -| page | pg | -| startOffset | so | -| endOffset | eo | -| duration | du | -| streamingResolutions | sr | -| raw | The string provided in raw will be added in the URL as it is. | -| flip | fl | - -### File Upload - -The SDK provides a simple interface using the `.upload()` method to upload files to the ImageKit Media Library. - -The `upload()` method requires mandatory `file` and the `fileName` parameter. In addition, it accepts all the parameters supported by the [ImageKit Upload API](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). - -Also, before making an upload request, please ensure you have generated mandatory security parameters: `signature`, `token`, and `expire`. To generate these security parameters, refer to the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload#signature-generation-for-client-side-file-upload). Obtain the parameters using a secure method and pass them, along with the mandatory `file` and `fileName` parameters, to the `upload()` method. - -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 specify tags for a file at the time of upload, use the `tags` parameter as specified in the [documentation here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). - - -#### Sample usage +#### Arithmetic Expressions in Transformations +```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" + }] +}); +``` + +### Supported Transformations + +The SDK supports various transformations which are translated to URL parameters as follows: + +| Transformation Name | URL Parameter | +| -------------------------- | ------------------------------------------------------------- | +| width | w | +| height | h | +| aspectRatio | ar | +| quality | q | +| aiRemoveBackground | e-bgremove | +| aiRemoveBackgroundExternal | e-removedotbg | +| aiUpscale | e-upscale | +| aiRetouch | e-retouch | +| aiVariation | e-genvar | +| aiDropShadow | e-dropshadow | +| aiChangeBackground | e-changebg | +| crop | c | +| cropMode | cm | +| x | x | +| y | y | +| xCenter | xc | +| yCenter | yc | +| focus | fo | +| format | f | +| radius | r | +| background | bg | +| border | b | +| rotation | rt | +| blur | bl | +| named | n | +| dpr | dpr | +| progressive | pr | +| lossless | lo | +| trim | t | +| metadata | md | +| colorProfile | cp | +| defaultImage | di | +| original | orig | +| videoCodec | vc | +| audioCodec | ac | +| grayscale | e-grayscale | +| contrastStretch | e-contrast | +| shadow | e-shadow | +| sharpen | e-sharpen | +| unsharpMask | e-usm | +| gradient | e-gradient | +| opacity | o | +| zoom | z | +| page | pg | +| startOffset | so | +| endOffset | eo | +| duration | du | +| streamingResolutions | sr | +| raw | The string provided in raw will be added in the URL as it is. | +| flip | fl | + +### Handling Unsupported Transformations + +If you specify a transformation parameter that is not explicitly supported by the SDK, it is added “as-is” in the generated URL. This provides flexibility for using new or custom transformations without waiting for an SDK update. + +## File Upload + +The SDK offers a simple interface via the `.upload()` method to upload files to the ImageKit Media Library. This method requires the following: +- **file** (mandatory) +- **fileName** (mandatory) +- Security parameters: **signature**, **token**, and **expire** + +Before invoking the upload, generate the necessary security parameters as per the [ImageKit Upload API documentation](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). + +### Basic Upload Example + +Below is an HTML form example that uses a callback for handling the upload response: + ```html -
+
- - + ``` -If the upload succeeds, `err` will be `null`, and the `result` will be the same as what is received from ImageKit's servers. -If the upload fails, `err` will be the same as what is received from ImageKit's servers, and the `result` will be null. - -## Tracking upload progress using custom XMLHttpRequest -You can use a custom XMLHttpRequest object as the following to bind `progress` or any other events for a customized implementation. +### Promise-based Upload Example +You can also use promises for a cleaner asynchronous approach: ```js -var fileSize = file.files[0].size; -var customXHR = new XMLHttpRequest(); -customXHR.upload.addEventListener('progress', function (e) { - if (e.loaded <= fileSize) { - var percent = Math.round(e.loaded / fileSize * 100); - console.log(`Uploaded ${percent}%`); - } - - if(e.loaded == e.total){ - console.log("Upload done"); - } -}); - imagekit.upload({ - xhr: customXHR, file: file.files[0], fileName: "abc1.jpg", - tags: ["tag1"], token: 'generated_token', signature: 'generated_signature', - expire: 'generated_expire', - extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } - ] + expire: 'generated_expire' }).then(result => { console.log(result); -}).then(error => { - console.log(error); -}) +}).catch(error => { + console.error(error); +}); ``` -## 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. +## Demo Application -```js -// Success -var response = await imagekit.upload({ - file: file.files[0], - fileName: "abc1.jpg", - tags: ["tag1"], - token: 'generated_token', - signature: 'generated_signature', - expire: 'generated_expire', - extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } - ] -}); -console.log(response.$ResponseMetadata.statusCode); // 200 - -// { 'content-length': "300", 'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} -console.log(response.$ResponseMetadata.headers); - -// Error -try { - await imagekit.upload({ - file: file.files[0], - fileName: "abc1.jpg", - tags: ["tag1"], - token: 'generated_token', - signature: 'generated_signature', - expire: 'generated_expire', - extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } - ] - }); -} catch (ex) { - console.log(response.$ResponseMetadata.statusCode); // 400 +For the fastest way to get started, check out the demo application in the [samples/sample-app](https://github.com/imagekit-developer/imagekit-javascript/tree/master/samples/sample-app) folder. - // {'content-type': 'application/json', 'x-request-id': 'ee560df4-d44f-455e-a48e-29dfda49aec5'} - console.log(response.$ResponseMetadata.headers); -} +To run the demo locally: +```bash +git clone https://github.com/imagekit-developer/imagekit-javascript.git +cd imagekit-javascript ``` +Then, create a `.env` file in the `samples/sample-app` directory based on `sample.env` and provide your `PRIVATE_KEY`, `PUBLIC_KEY`, and `URL_ENDPOINT` from your ImageKit dashboard. Finally, start the demo: +```bash +yarn startSampleApp +``` + +## Changelog + +For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). From b9e554d14e926c9d318163052e8326e17807b73c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 09:51:29 +0530 Subject: [PATCH 067/166] change badge position --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f465a47..f42aa71 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ # ImageKit.io JavaScript SDK +![gzip size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=gzip&label=gzip) +![brotli size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=brotli&label=brotli) +![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) +[![npm version](https://img.shields.io/npm/v/imagekit-javascript)](https://www.npmjs.com/package/imagekit-javascript) +[![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) +[![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) + Lightweight JavaScript SDK for generating optimized URLs for images and videos, and for handling file uploads via ImageKit. ## Table of Contents @@ -18,14 +26,6 @@ Lightweight JavaScript SDK for generating optimized URLs for images and videos, - [Demo Application](#demo-application) - [Changelog](#changelog) -![gzip size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=gzip&label=gzip) -![brotli size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=brotli&label=brotli) -![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) -[![npm version](https://img.shields.io/npm/v/imagekit-javascript)](https://www.npmjs.com/package/imagekit-javascript) -[![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) -[![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) - ## Installation ### Using npm From 02751f37319fc40ac395bf9cf1ecb7bc4c3f6f45 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 10:00:49 +0530 Subject: [PATCH 068/166] docs: enhance README with detailed SDK initialization and transformation usage --- README.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f42aa71..7a36e6e 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ And include it in your HTML: ## Initialization -Initialize the SDK with your URL endpoint. For URL generation: +Initialize the SDK by specifying your URL endpoint. You can obtain your URL endpoint from [https://imagekit.io/dashboard/url-endpoints](https://imagekit.io/dashboard/url-endpoints) and your public API key from [https://imagekit.io/dashboard/developer/api-keys](https://imagekit.io/dashboard/developer/api-keys). For URL generation: ```js var imagekit = new ImageKit({ urlEndpoint: "https://ik.imagekit.io/your_imagekit_id" @@ -201,7 +201,11 @@ var imageURL = imagekit.url({ ### Supported Transformations -The SDK supports various transformations which are translated to URL parameters as follows: +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. + +Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. | Transformation Name | URL Parameter | | -------------------------- | ------------------------------------------------------------- | @@ -209,8 +213,8 @@ The SDK supports various transformations which are translated to URL parameters | height | h | | aspectRatio | ar | | quality | q | -| aiRemoveBackground | e-bgremove | -| aiRemoveBackgroundExternal | e-removedotbg | +| aiRemoveBackground | e-bgremove (ImageKit powered) | +| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | | aiUpscale | e-upscale | | aiRetouch | e-retouch | | aiVariation | e-genvar | @@ -246,6 +250,7 @@ The SDK supports various transformations which are translated to URL parameters | sharpen | e-sharpen | | unsharpMask | e-usm | | gradient | e-gradient | +| flip | fl | | opacity | o | | zoom | z | | page | pg | @@ -254,12 +259,22 @@ The SDK supports various transformations which are translated to URL parameters | duration | du | | streamingResolutions | sr | | raw | The string provided in raw will be added in the URL as it is. | -| flip | fl | ### Handling Unsupported Transformations If you specify a transformation parameter that is not explicitly supported by the SDK, it is added “as-is” in the generated URL. This provides flexibility for using new or custom transformations without waiting for an SDK update. +For example: +```js +var imageURL = imagekit.url({ + path: "/test_path.jpg", + transformation: [{ + "newparam": "cool" + }] +}); +// Generated URL: https://ik.imagekit.io/test_url_endpoint/tr:newparam-cool/test_path.jpg +``` + ## File Upload The SDK offers a simple interface via the `.upload()` method to upload files to the ImageKit Media Library. This method requires the following: From 010e9a04e2c3e25301b222f9bd2e5e53f4ab6dbf Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 10:08:15 +0530 Subject: [PATCH 069/166] docs: update README with quick demo references and remove outdated instructions --- README.md | 14 +++----------- test/url-generation.js | 2 ++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7a36e6e..e45b80b 100644 --- a/README.md +++ b/README.md @@ -338,17 +338,9 @@ imagekit.upload({ ## Demo Application -For the fastest way to get started, check out the demo application in the [samples/sample-app](https://github.com/imagekit-developer/imagekit-javascript/tree/master/samples/sample-app) folder. - -To run the demo locally: -```bash -git clone https://github.com/imagekit-developer/imagekit-javascript.git -cd imagekit-javascript -``` -Then, create a `.env` file in the `samples/sample-app` directory based on `sample.env` and provide your `PRIVATE_KEY`, `PUBLIC_KEY`, and `URL_ENDPOINT` from your ImageKit dashboard. Finally, start the demo: -```bash -yarn startSampleApp -``` +For a quick demonstration of the SDK features, refer to our test examples: +- URL Generation examples can be found in [test/url-generation.js](./test/url-generation.js) +- File Upload examples can be found in [test/upload.js](./test/upload.js) ## Changelog diff --git a/test/url-generation.js b/test/url-generation.js index 126a85d..40c7e38 100644 --- a/test/url-generation.js +++ b/test/url-generation.js @@ -867,6 +867,7 @@ describe("URL generation", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:yc-40/test_path1.jpg`); }); + // This is done just to test how SDK constructs URL, the actual transformation is not valid. it('Including deprecated properties', function () { const url = imagekit.url({ path: "/test_path.jpg", @@ -906,6 +907,7 @@ describe("URL generation", function () { 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,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); }); + // This is done just to test how SDK constructs URL, the actual transformation is not valid it('should generate the correct URL when comprehensive transformations, including video and AI transformations, are applied', function () { const url = imagekit.url({ path: "/test_path.jpg", From 2ccfcf2187394fcd7356ffac5345e71dfa5c3750 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 10:08:22 +0530 Subject: [PATCH 070/166] docs: update README to rename 'Demo Application' section to 'Test Examples' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e45b80b..2b06eea 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Lightweight JavaScript SDK for generating optimized URLs for images and videos, - [File Upload](#file-upload) - [Basic Upload Example](#basic-upload-example) - [Promise-based Upload Example](#promise-based-upload-example) -- [Demo Application](#demo-application) +- [Test Examples](#test-examples) - [Changelog](#changelog) ## Installation @@ -336,7 +336,7 @@ imagekit.upload({ }); ``` -## Demo Application +## Test Examples For a quick demonstration of the SDK features, refer to our test examples: - URL Generation examples can be found in [test/url-generation.js](./test/url-generation.js) From 8b3067d353199744364e5e36338d749d09ca45d4 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 10:33:54 +0530 Subject: [PATCH 071/166] feat: add static file serving and update image handling in Pug template --- samples/sample-app/server/server.js | 1 + samples/sample-app/views/index.pug | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/samples/sample-app/server/server.js b/samples/sample-app/server/server.js index 5942f56..6558660 100644 --- a/samples/sample-app/server/server.js +++ b/samples/sample-app/server/server.js @@ -8,6 +8,7 @@ const path = require('path'); const pugTemplatePath = path.join(__dirname, "../views/index.pug"); const app = express(); +app.use(express.static('static')) app.use(cors()); app.set('view engine', 'pug'); diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug index 6f0b44f..91d398f 100644 --- a/samples/sample-app/views/index.pug +++ b/samples/sample-app/views/index.pug @@ -1,5 +1,6 @@ html body + img(id="my-image") h3 Imagekit Demo form(action='#' onSubmit='upload(event)') input(type='file' id='file1') @@ -18,7 +19,8 @@ html p img(src="") - script(type='text/javascript' src="https://unpkg.com/imagekit-javascript/dist/imagekit.min.js") + // Copy paste manually from dist + script(type='text/javascript' src="./imagekit.min.js") script. try { var imagekit = new ImageKit({ @@ -26,8 +28,20 @@ html urlEndpoint: "!{urlEndpoint}", }); + window.imagekit = imagekit; + var url = imagekit.url({ + src: "https://ik.imagekit.io/demo/default-image.jpg", + transformation: [{ + height: 100 + }] + }) + + var img = document.getElementById("my-image"); + console.log(url); + img.src = url; + function upload(e) { e.preventDefault(); @@ -119,4 +133,4 @@ html } } catch(ex) { console.log(ex); - } \ No newline at end of file + } From 7530fd3db0f41e013f84ab6290628dfff87d9d4e Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 19 Mar 2025 10:41:15 +0530 Subject: [PATCH 072/166] docs: update CHANGELOG with corrected links for overlay syntax and file upload parameters --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aace157..03d7092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,10 @@ ### Breaking Changes - **Overlay Syntax Update** - In version 3.0.0, the old overlay syntax parameters for transformations (e.g., `oi`, `ot`, `obg`, and others as detailed [here](https://docs.imagekit.io/features/image-transformations/overlay)) have been removed. These parameters are deprecated and will now return errors when used in URLs. + In version 3.0.0, the old overlay syntax parameters for transformations (e.g., `oi`, `ot`, `obg`, and others as detailed [here](https://imagekit.io/docs/add-overlays-on-images)) have been removed. These parameters are deprecated and will now return errors when used in URLs. **Action Required:** - Migrate to the new layers syntax which supports overlay nesting, offers improved positional control, and allows more transformations at the layer level. For a quick start, refer to the [examples](https://docs.imagekit.io/features/image-transformations/overlay-using-layers#examples). Use the `raw` transformation parameter to implement this migration. + Migrate to the new layers syntax which supports overlay nesting, offers improved positional control, and allows more transformations at the layer level. For a quick start, refer to the [examples](https://imagekit.io/docs/add-overlays-on-images). Use the `raw` transformation parameter to implement this migration. --- @@ -20,4 +20,4 @@ Previously, client-side file uploads required the SDK to be initialized with both the `publicKey` and `authenticationEndpoint` to fetch security parameters (`signature`, `token`, and `expire`). **Action Required:** - With version 2.0.0, you must now generate the security parameters (`signature`, `token`, and `expire`) yourself, eliminating the need for the `authenticationEndpoint`. When invoking the SDK's upload method, be sure to include these parameters along with other [upload options](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload). For further details, consult the documentation [here](https://docs.imagekit.io/api-reference/upload-file-api/client-side-file-upload#signature-generation-for-client-side-file-upload). + With version 2.0.0, you must now generate the security parameters (`signature`, `token`, and `expire`) yourself, eliminating the need for the `authenticationEndpoint`. When invoking the SDK's upload method, be sure to include these parameters along with other [upload options](https://imagekit.io/docs/api-reference/upload-file/upload-file#Request). For further details, consult the documentation [here](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). From 751bf46c71c15447dd1b4e0f7cb2bfd3f03b0d5f Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 21 Mar 2025 18:05:22 +0700 Subject: [PATCH 073/166] add type defination for overlay --- src/interfaces/Transformation.ts | 129 ++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index a963f8f..b5d942f 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -1,6 +1,6 @@ export type TransformationPosition = "path" | "query"; -type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440" | "2160"; +export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440" | "2160"; /** * The SDK provides easy to use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. @@ -421,6 +421,133 @@ export interface Transformation { * @deprecated Use `gradient` instead. */ effectGradient?: string; + + /** + * Overlay to be applied on the parent image or video. ImageKit allows you to overlay images, text, videos, subtitles, and solid colors on the parent image or video. + * + * {@link https://imagekit.io/docs/transformations#overlay-using-layers} + */ + overlay?: Overlay; +} + +export interface OverlayPosition { + /** + * `x` of the top-left corner in the base asset where the layer's top-left corner would be placed. It can also accept arithmetic expressions such as `bw_mul_0.4`, or `bw_sub_cw`. + * + * It maps to `lx` in the URL. + * + * Learn about [Arthmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + */ + x?: number | string; + + /** + * `y` of the top-left corner in the base asset where the layer's top-left corner would be placed. It can also accept arithmetic expressions such as `bh_mul_0.4`, or `bh_sub_ch`. + * + * It maps to `ly` in the URL. + * + * Learn about [Arthmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + */ + y?: number | string; + + /** + * Position of the overlay in relation to the parent image or video. The overlay can be positioned at the center, top, left, bottom, right, top_left, top_right, bottom_left, or bottom_right of the parent image or video. + * + * This maps to `lfo` in the URL. + */ + focus?: `center` | `top` | `left` | `bottom` | `right` | `top_left` | `top_right` | `bottom_left` | `bottom_right`; +} + +export interface OverlayTiming { + /** + * Start time of the base video in seconds when the layer should appear. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * + * It maps to `lso` in the URL. + */ + start?: number | string; + + /** + * Duration in seconds during which layer should appear on the base video. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * + * It maps to `ldu` in the URL. + */ + duration?: number | string; + + /** + * End time of the base video when this layer should disappear. In case both `end` and `duration` are present, `duration` is ignored. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * + * It maps to `leo` in the URL. + */ + end?: number | string; } +interface BaseOverlay { + /** + * Positioning relative to parent. Accepts a JSON object with `x` and `y` (or `focus`) properties. + * + * {@link https://imagekit.io/docs/transformations#position-of-layer} + */ + position?: OverlayPosition; + + /** + * Timing (only valid if parent/base is a video). Accepts a JSON object with `start` (lso), `end` (leo), and `duration` (ldu) properties. + * + * {@link https://imagekit.io/docs/transformations#position-of-layer} + */ + timing?: OverlayTiming; + + /** + * Array of transformations to be applied to this overlay. Support of supported transformations also depends on the type of base and overlay asset. Refer to the docs below for more information. + */ + transformations?: Transformation[]; +} + + +export interface TextOverlay extends BaseOverlay { + type: "text"; + + /** + * Text to be displayed in the overlay. The SDK will automatically handle special characters and URL encoding for you. + */ + text: string; +} + +export interface ImageOverlay extends BaseOverlay { + type: "image"; + + /** + * Relative path to the image to be used as an overlay. + */ + input: string; +} + +export interface VideoOverlay extends BaseOverlay { + type: "video"; + /** + * Relative path to the video to be used as an overlay. + */ + input: string; +} + +export interface SubtitleOverlay extends BaseOverlay { + type: "subtitle"; + /** + * Relative path to the subtitle file to be used as an overlay. + */ + input: string; +} + +export interface SolidColorOverlay extends BaseOverlay { + type: "solidColor"; + /** + * It is used to specify the color of the block in RGB Hex Code (e.g. `FF0000`), or an RGBA Code (e.g. `FFAABB50`), or a color name (e.g. `red`). If you specify an 8-character background, the last two characters must be a number between `00` and `99`, which indicates the opacity level of the background. `00` represents an opacity level of `0.00`, `01` represents an opacity level of `0.01`, and so on. + */ + color: string; +} + +export type Overlay = + | TextOverlay + | ImageOverlay + | VideoOverlay + | SubtitleOverlay + | SolidColorOverlay; From 09b31c4ff1298d8490f448b7e60ffcf8ff1aa039 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 21 Mar 2025 18:35:38 +0700 Subject: [PATCH 074/166] Improved JSDocs --- src/interfaces/Transformation.ts | 332 ++++++++++++++++--------------- 1 file changed, 171 insertions(+), 161 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index b5d942f..a0e418f 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -3,385 +3,382 @@ export type TransformationPosition = "path" | "query"; export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440" | "2160"; /** - * The SDK provides easy to use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. + * The SDK provides easy-to-use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, you can use the `raw` parameter to pass the transformation string directly. * - * {@link https://imagekit.io/docs/transformations} + * {@link https://imagekit.io/docs/transformations|Transformations Documentation} */ export interface Transformation { /** - * The width of the output. If a value between 0 and 1 is used, it’s treated - * as a percentage (e.g., `0.4` -> 40% of original width). You can also supply - * arithmetic expressions (e.g., `"iw_div_2"`). + * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage + * (e.g., `0.4` represents 40% of the original width). You can also supply arithmetic expressions (e.g., `iw_div_2`). * - * {@link https://imagekit.io/docs/image-resize-and-crop#width---w} + * {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Image Resize and Crop - Width} */ width?: number | string; /** - * The height of the output. If a value between 0 and 1 is used, it’s treated - * as a percentage (e.g., `0.5` -> 50% of original height). You can also supply - * arithmetic expressions (e.g., `"ih_mul_0.5"`). + * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage + * (e.g., `0.5` represents 50% of the original height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). * - * {@link https://imagekit.io/docs/image-resize-and-crop#height---h} + * {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Image Resize and Crop - Height} */ height?: number | string; /** - * Specifies the aspect ratio for the output, e.g., `"ar-4-3"`. - * Typically used with either width or height (not both). - * Example usage: `aspectRatio = "4:3"` or `"4_3"` or an expression like `"iar_div_2"`. + * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). + * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. * - * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar} + * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar|Image Resize and Crop - Aspect Ratio} */ aspectRatio?: number | string; /** - * Specify the background that can be used along with some cropping strategies while resizing an image: - * - A solid color: `"red"`, `"F3F3F3"`, `"AAFF0010"`. + * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. + * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background} + * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background|Effects and Enhancements - Solid Color Background} * - * - A blurred background: `"blurred"`, `"blurred_25_N15"`, etc. + * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. * - * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background} + * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background|Effects and Enhancements - Blurred Background} * - * - Expand the image boundaries using generative fill: `genfill`. Optionally control the background scene by passing text prompt: `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. + * - Expand the image boundaries using generative fill: `genfill`. Optionally, control the background scene by passing a text prompt: + * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. * - * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill} + * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill|AI Transformations - Generative Fill Background} */ background?: string; /** - * Add a border to the output media. Accepts `_`, - * e.g. `"5_FFF000"` (5px yellow border), or an expression like `"ih_div_20_FF00FF"`. + * Adds a border to the output media. Accepts a string in the format `_` + * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#border---b} + * {@link https://imagekit.io/docs/effects-and-enhancements#border---b|Effects and Enhancements - Border} */ border?: string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus} + * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} */ crop?: "force" | "at_max" | "at_max_enlarge" | "at_least" | "maintain_ratio"; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus} + * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} */ cropMode?: "pad_resize" | "extract" | "pad_extract"; /** - * Possible values 0.1 to 5 or `auto` for automatic DPR calculation. + * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio (DPR) calculation. * - * {@link https://imagekit.io/docs/image-resize-and-crop#dpr---dpr} + * {@link https://imagekit.io/docs/image-resize-and-crop#dpr---dpr|Image Resize and Crop - DPR} */ dpr?: number /** - * This parameter can be used along with pad resize, maintain ratio, or extract crop to change the behavior of padding or cropping + * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. * - * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo} + * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo|Image Resize and Crop - Focus} */ focus?: string; /** - * Used to specify the quality of the output image for lossy formats like JPEG, WebP, and AVIF. A large quality number indicates a larger output image size with high quality. A small quality number indicates a smaller output image size with lower quality. + * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. + * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. * - * {@link https://imagekit.io/docs/image-optimization#quality---q} + * {@link https://imagekit.io/docs/image-optimization#quality---q|Image Optimization - Quality} */ quality?: number; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates} + * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} */ x?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates} + * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} */ xCenter?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates} + * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} */ y?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates} + * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} */ yCenter?: number | string; /** - * Output format for images or videos, e.g., `"jpg"`, `"png"`, `"webp"`, `"mp4"`, `"auto"`. You can also pass `orig` which works only for images and will return the image in the original format. + * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. + * You can also pass `orig` for images to return the original format. + * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. * - * ImageKit will automatically deliver images and videos in best possible format based on the device support unless you disable it from the dashboard settings or override it using the `format` parameter. - * - * {@link https://imagekit.io/docs/image-optimization#format---f} - * - * {@link https://imagekit.io/docs/video-optimization#format---f}} + * {@link https://imagekit.io/docs/image-optimization#format---f|Image Optimization - Format} & {@link https://imagekit.io/docs/video-optimization#format---f|Video Optimization - Format} */ format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; /** - * Video codec, e.g., `"h264"`, `"vp9"`, `"av1"` or `"none"`. + * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. * - * {@link https://imagekit.io/docs/video-optimization#video-codec---vc} + * {@link https://imagekit.io/docs/video-optimization#video-codec---vc|Video Optimization - Video Codec} */ videoCodec?: "h264" | "vp9" | "av1" | "none"; /** - * Audio codec, e.g., `"aac"`, `"opus"` or `"none"`. + * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. * - * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac} + * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac|Video Optimization - Audio Codec} */ audioCodec?: "aac" | "opus" | "none"; /** - * Corner radius for rounded corners (e.g., `20`) or `"max"` for circular/oval shapes. + * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. * - * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r} + * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r|Effects and Enhancements - Radius} */ radius?: number | "max"; /** - * Rotation in degrees. Positive values rotate clockwise; you can - * also use e.g. `"N40"` for counterclockwise or `"auto"` to read EXIF data. - * For videos only 0 , 90 , 180 , 270 and 360 values are supported. + * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation + * or `auto` to use the orientation specified in the image's EXIF data. + * For videos, only the following values are supported: 0, 90, 180, 270, or 360. * - * {@link https://imagekit.io/docs/effects-and-enhancements#rotate---rt} + * {@link https://imagekit.io/docs/effects-and-enhancements#rotate---rt|Effects and Enhancements - Rotate} */ rotation?: number | string; /** - * Gaussian blur level. Ranges 1–100 or an expression like `"bl-10"`. Possible values include integers between 1 and 100. + * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl} + * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl|Effects and Enhancements - Blur} */ blur?: number; /** - * {@link https://imagekit.io/docs/transformations#named-transformations} + * {@link https://imagekit.io/docs/transformations#named-transformations|Transformations - Named Transformations} */ named?: string; /** - * Fallback image if the resource is not found, e.g., a URL or path. + * Specifies a fallback image if the resource is not found, e.g., a URL or file path. * - * {@link https://imagekit.io/docs/image-transformation#default-image---di} + * {@link https://imagekit.io/docs/image-transformation#default-image---di|Image Transformation - Default Image} */ defaultImage?: string; /** - * It is used to flip/mirror an image horizontally, vertically, or in both directions. - * Possible values - h (horizontal), v (vertical), h_v (horizontal and vertical) + * Flips or mirrors an image either horizontally, vertically, or both. + * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl} + * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl|Effects and Enhancements - Flip} */ flip?: "h" | "v" | "h_v" | "v_h"; /** - * Whether to serve the original file without any transformations if `true`. + * If set to true, serves the original file without applying any transformations. * - * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true} + * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true|Core Delivery Features - Deliver Original File As Is} */ original?: boolean; /** - * Start offset (in seconds) for trimming videos. e.g., `5` or `"10.5"`. - * Also supports arithmetic expressions. + * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Arithmetic expressions are also supported. * - * {@link https://imagekit.io/docs/trim-videos#start-offset---so} + * {@link https://imagekit.io/docs/trim-videos#start-offset---so|Trim Videos - Start Offset} */ startOffset?: number | string; /** - * End offset (in seconds) for trimming videos. e.g., `5` or `"10.5"`. - * Usually used with `startOffset` to define a time window. - * Also supports arithmetic expressions. + * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to define a time window. Arithmetic expressions are supported. * - * {@link https://imagekit.io/docs/trim-videos#end-offset---eo} + * {@link https://imagekit.io/docs/trim-videos#end-offset---eo|Trim Videos - End Offset} */ endOffset?: number | string; /** - * Duration (in seconds) for trimming videos. e.g., `5` or `"10.5"`. - * Typically used with `startOffset` to specify length from the start point. - * Also supports arithmetic expressions. + * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. * - * {@link https://imagekit.io/docs/trim-videos#duration---du} + * {@link https://imagekit.io/docs/trim-videos#duration---du|Trim Videos - Duration} */ duration?: number | string; /** - * Provide an array of resolutions (e.g. `["240", "360", "480", "720", "1080"]`). + * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. * - * {@link https://imagekit.io/docs/adaptive-bitrate-streaming} + * {@link https://imagekit.io/docs/adaptive-bitrate-streaming|Adaptive Bitrate Streaming} */ streamingResolutions?: StreamingResolution[]; /** - * Enable grayscale effect for images. + * Enables a grayscale effect for images. * - * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale} + * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale|Effects and Enhancements - Grayscale} */ grayscale?: true; /** - * Upscale images beyond their original dimensions with AI. + * Upscales images beyond their original dimensions using AI. * - * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale} + * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} */ aiUpscale?: true /** - * Retouch (AI-based) for improving faces or product shots. + * Performs AI-based retouching to improve faces or product shots. * - * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch} + * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} */ aiRetouch?: true /** - * Generate variation of an image using AI. This will generate a new image with slight variations from the original image. The variations include changes in color, texture, and other visual elements. However, the model will try to preserve the structure and essence of the original image. + * Generates a variation of an image using AI. This produces a new image with slight variations from the original, + * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. * - * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar} + * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} */ aiVariation?: true /** - * Add an AI-based drop shadow around a foreground object on a transparent or removed background. - * Optionally, you can control the direction, elevation, and saturation of the light source. E.g. change light direction `az-45`. - * - * Pass `true` for default drop shadow or a string for custom drop shadow. + * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. + * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). + * Pass true for the default drop shadow, or provide a string for a custom drop shadow. * - * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow} + * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow|AI Transformations - Drop Shadow} */ aiDropShadow?: true | string /** - * Change background using AI. Provide a prompt or base64-encoded prompt. e.g. `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, + * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. * - * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg} + * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg|AI Transformations - Change Background} */ aiChangeBackground?: string; /** - * ImageKit’s in-house background removal. + * Applies ImageKit’s in-house background removal. * - * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove} + * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} */ aiRemoveBackground?: true /** - * Use third-party background removal. Use `aiRemoveBackground` - ImageKit's in-house background removal which is 90% cheaper. + * Uses third-party background removal. + * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. * - * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg} + * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg|AI Transformations - External Background Removal} */ aiRemoveBackgroundExternal?: true /** - * Auto-enhance contrast for an image (contrast stretch). + * Automatically enhances the contrast of an image (contrast stretch). * - * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast} + * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast|Effects and Enhancements - Contrast Stretch} */ contrastStretch?: true /** - * This adds a shadow under solid objects in an input image with a transparent background. Check `eDropshadow` for AI-based shadows. + * Adds a shadow beneath solid objects in an image with a transparent background. + * For AI-based drop shadows, refer to aiDropShadow. + * Pass true for a default shadow, or provide a string for a custom shadow. * - * Pass `true` for default shadow or a string for custom shadow. - * - * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow} + * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow|Effects and Enhancements - Shadow} */ shadow?: true | string /** - * It is used to sharpen the input image. It is useful when highlighting the edges and finer details within an image. - * - * Pass `true` for default sharpening or a number for custom sharpening. + * Sharpens the input image, highlighting edges and finer details. + * Pass true for default sharpening, or provide a numeric value for custom sharpening. * - * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen} + * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} */ sharpen?: true | number /** - * Unsharp Masking (USM) is an image sharpening technique. This transform allows you to apply and control unsharp masks on your images. + * Applies Unsharp Masking (USM), an image sharpening technique. + * Pass true for a default unsharp mask, or provide a string for a custom unsharp mask. * - * Pass `true` for default unsharp mask or a string for custom unsharp mask. - * - * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm} + * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} */ unsharpMask?: true | string; /** - * The gradient formed is a linear gradient containing two colors, and it can be customized. - * - * Pass `true` for default gradient or a string for custom gradient. + * Creates a linear gradient with two colors. Pass true for a default gradient, or provide a string for a custom gradient. * - * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient} + * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} */ gradient?: true | string; /** - * Used to specify whether the output JPEG image must be rendered progressively. In progressive loading, the output image renders as a low-quality pixelated full image, which, over time, keeps on adding more pixels and information to the image. This helps you maintain a fast perceived load time. + * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, + * pixelated version of the full image, which gradually improves to provide a faster perceived load time. * - * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr} + * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr|Image Optimization - Progressive Image} */ progressive?: boolean; /** - * Used to specify whether the output image (if in JPEG or PNG) must be compressed losslessly. + * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. * - * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo} + * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo|Image Optimization - Lossless Compression} */ lossless?: boolean /** - * It specifies whether the output image should contain the color profile initially available with the original image. + * Indicates whether the output image should retain the original color profile. * - * {@link https://imagekit.io/docs/image-optimization#color-profile---cp} + * {@link https://imagekit.io/docs/image-optimization#color-profile---cp|Image Optimization - Color Profile} */ colorProfile?: boolean; /** - * By default, ImageKit removes all metadata as part of automatic image compression. Set this to `true` to preserve metadata. + * By default, ImageKit removes all metadata during automatic image compression. + * Set this to true to preserve metadata. * - * {@link https://imagekit.io/docs/image-optimization#image-metadata---md} + * {@link https://imagekit.io/docs/image-optimization#image-metadata---md|Image Optimization - Image Metadata} */ metadata?: boolean; /** - * It is used to specify the opacity level of the output image. + * Specifies the opacity level of the output image. * - * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o} + * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o|Effects and Enhancements - Opacity} */ opacity?: number; /** - * Useful with images that have a solid or nearly solid background with the object in the center. This parameter trims the background from the image, leaving only the central object in the output image. + * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, + * leaving only the central object in the output image. * - * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t} + * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t|Effects and Enhancements - Trim Edges} */ trim?: true | number; /** - * This parameter accepts a number that determines how much to zoom in or out of the cropped area. - * It must be used along with fo-face or fo- + * Accepts a numeric value that determines how much to zoom in or out of the cropped area. + * It should be used in conjunction with fo-face or fo-. * - * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z} + * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z|Image Resize and Crop - Zoom} */ zoom?: number; /** - * Extract specific page/frame from multi-page or layered files (PDF, PSD, AI), - * Pick by number e.g., `2`. Or 2nd and 3rd layers combined using `3-4`. - * Or pick a layer from PSD by name, e.g., `name-layer-4`. + * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). + * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), + * or by name (e.g., `name-layer-4` for a PSD layer). * - * {@link https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files} + * {@link https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files|Vector and Animated Images - Thumbnail Extraction} */ page?: number | string; /** - * Pass any transformation that is not directly supported by the SDK. This transformation is passed as it is in the URL. + * Pass any transformation not directly supported by the SDK. + * This transformation string is appended to the URL as provided. */ raw?: string; @@ -423,59 +420,67 @@ export interface Transformation { effectGradient?: string; /** - * Overlay to be applied on the parent image or video. ImageKit allows you to overlay images, text, videos, subtitles, and solid colors on the parent image or video. + * Specifies an overlay to be applied on the parent image or video. + * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. * - * {@link https://imagekit.io/docs/transformations#overlay-using-layers} + * {@link https://imagekit.io/docs/transformations#overlay-using-layers|Transformations - Overlay Using Layers} */ overlay?: Overlay; } export interface OverlayPosition { /** - * `x` of the top-left corner in the base asset where the layer's top-left corner would be placed. It can also accept arithmetic expressions such as `bw_mul_0.4`, or `bw_sub_cw`. - * - * It maps to `lx` in the URL. + * Specifies the x-coordinate of the top-left corner of the base asset where the overlay's top-left corner will be positioned. + * It also accepts arithmetic expressions such as `bw_mul_0.4` or `bw_sub_cw`. + * Maps to `lx` in the URL. * - * Learn about [Arthmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + * Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) */ x?: number | string; /** - * `y` of the top-left corner in the base asset where the layer's top-left corner would be placed. It can also accept arithmetic expressions such as `bh_mul_0.4`, or `bh_sub_ch`. + * Specifies the y-coordinate of the top-left corner of the base asset where the overlay's top-left corner will be positioned. + * It also accepts arithmetic expressions such as `bh_mul_0.4` or `bh_sub_ch`. + * Maps to `ly` in the URL. * - * It maps to `ly` in the URL. - * - * Learn about [Arthmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + * Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) */ y?: number | string; /** - * Position of the overlay in relation to the parent image or video. The overlay can be positioned at the center, top, left, bottom, right, top_left, top_right, bottom_left, or bottom_right of the parent image or video. - * - * This maps to `lfo` in the URL. + * Specifies the position of the overlay relative to the parent image or video. + * Acceptable values: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. + * Maps to `lfo` in the URL. */ focus?: `center` | `top` | `left` | `bottom` | `right` | `top_left` | `top_right` | `bottom_left` | `bottom_right`; } export interface OverlayTiming { /** - * Start time of the base video in seconds when the layer should appear. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * Specifies the start time (in seconds) for when the overlay should appear on the base video. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. * - * It maps to `lso` in the URL. + * Maps to `lso` in the URL. */ start?: number | string; /** - * Duration in seconds during which layer should appear on the base video. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * Specifies the duration (in seconds) during which the overlay should appear on the base video. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. * - * It maps to `ldu` in the URL. + * Maps to `ldu` in the URL. */ duration?: number | string; /** - * End time of the base video when this layer should disappear. In case both `end` and `duration` are present, `duration` is ignored. It accepts a positive number upto two decimal e.g. 20 or 20.50. Only applicable if parent layer or base is video. It can also accept arithmetic expressions such as `bdu_mul_0.4`, or `bdu_sub_idu`. Learn more about arithmetic expressions [here](/arithmetic-expressions-in-transformations). + * Specifies the end time (in seconds) for when the overlay should disappear from the base video. + * If both end and duration are provided, duration is ignored. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. * - * It maps to `leo` in the URL. + * Maps to `leo` in the URL. */ end?: number | string; } @@ -483,21 +488,24 @@ export interface OverlayTiming { interface BaseOverlay { /** - * Positioning relative to parent. Accepts a JSON object with `x` and `y` (or `focus`) properties. + * Specifies the overlay's position relative to the parent asset. + * Accepts a JSON object with `x` and `y` (or `focus`) properties. * - * {@link https://imagekit.io/docs/transformations#position-of-layer} + * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} */ position?: OverlayPosition; /** - * Timing (only valid if parent/base is a video). Accepts a JSON object with `start` (lso), `end` (leo), and `duration` (ldu) properties. + * Specifies timing information for the overlay (only applicable if the base asset is a video). + * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. * - * {@link https://imagekit.io/docs/transformations#position-of-layer} + * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} */ timing?: OverlayTiming; /** - * Array of transformations to be applied to this overlay. Support of supported transformations also depends on the type of base and overlay asset. Refer to the docs below for more information. + * An array of transformations to be applied to the overlay. + * The supported transformations depend on the type of the base and overlay asset. */ transformations?: Transformation[]; } @@ -507,7 +515,8 @@ export interface TextOverlay extends BaseOverlay { type: "text"; /** - * Text to be displayed in the overlay. The SDK will automatically handle special characters and URL encoding for you. + * Specifies the text to be displayed in the overlay. + * The SDK automatically handles special characters and URL encoding. */ text: string; } @@ -516,7 +525,7 @@ export interface ImageOverlay extends BaseOverlay { type: "image"; /** - * Relative path to the image to be used as an overlay. + * Specifies the relative path to the image used as an overlay. */ input: string; } @@ -524,7 +533,7 @@ export interface ImageOverlay extends BaseOverlay { export interface VideoOverlay extends BaseOverlay { type: "video"; /** - * Relative path to the video to be used as an overlay. + * Specifies the relative path to the video used as an overlay. */ input: string; } @@ -532,7 +541,7 @@ export interface VideoOverlay extends BaseOverlay { export interface SubtitleOverlay extends BaseOverlay { type: "subtitle"; /** - * Relative path to the subtitle file to be used as an overlay. + * Specifies the relative path to the subtitle file used as an overlay. */ input: string; } @@ -540,7 +549,8 @@ export interface SubtitleOverlay extends BaseOverlay { export interface SolidColorOverlay extends BaseOverlay { type: "solidColor"; /** - * It is used to specify the color of the block in RGB Hex Code (e.g. `FF0000`), or an RGBA Code (e.g. `FFAABB50`), or a color name (e.g. `red`). If you specify an 8-character background, the last two characters must be a number between `00` and `99`, which indicates the opacity level of the background. `00` represents an opacity level of `0.00`, `01` represents an opacity level of `0.01`, and so on. + * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name (e.g., `red`). + * If an 8-character value is provided, the last two characters represent the opacity level (from `00` for 0.00 to `99` for 0.99). */ color: string; } From 9769f409e1ec84ea37e45c1fabc746f6164e5392 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 21 Mar 2025 18:44:10 +0700 Subject: [PATCH 075/166] fix: update test script to include all test files in subdirectories and split the test script into multiple test scripts --- package.json | 2 +- test/{url-generation.js => url-generation/basic.js} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename test/{url-generation.js => url-generation/basic.js} (99%) diff --git a/package.json b/package.json index 1938f2e..1bbfb44 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "dev": "rollup -c -w", "export-types": "tsc", "build": "rm -rf dist*;rollup -c && yarn export-types", - "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha test/*.js", + "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha \"test/**/*.js\"", "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", "report-coverage": "codecov" }, diff --git a/test/url-generation.js b/test/url-generation/basic.js similarity index 99% rename from test/url-generation.js rename to test/url-generation/basic.js index 40c7e38..739f240 100644 --- a/test/url-generation.js +++ b/test/url-generation/basic.js @@ -1,9 +1,9 @@ const chai = require("chai"); -const pkg = require("../package.json"); +const pkg = require("../../package.json"); global.FormData = require('formdata-node'); const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../src/index"; +const initializationParams = require("../data").initializationParams +import ImageKit from "../../src/index"; describe("URL generation", function () { From 2298a72779c3ddf71174f07c6764a83b4aed9748 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 21 Mar 2025 20:15:57 +0700 Subject: [PATCH 076/166] add transformation types for different type of overlays --- src/interfaces/Transformation.ts | 235 +++++++++++++++++++++++++------ 1 file changed, 192 insertions(+), 43 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index a0e418f..4909bf5 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -13,7 +13,7 @@ export interface Transformation { * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage * (e.g., `0.4` represents 40% of the original width). You can also supply arithmetic expressions (e.g., `iw_div_2`). * - * {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Image Resize and Crop - Width} + * Width transformation - {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#width---w|Videos} */ width?: number | string; @@ -21,7 +21,7 @@ export interface Transformation { * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage * (e.g., `0.5` represents 50% of the original height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). * - * {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Image Resize and Crop - Height} + * Height transformation - {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#height---h|Videos} */ height?: number | string; @@ -43,7 +43,7 @@ export interface Transformation { * * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background|Effects and Enhancements - Blurred Background} * - * - Expand the image boundaries using generative fill: `genfill`. Optionally, control the background scene by passing a text prompt: + * - Expand the image boundaries using generative fill: `genfill`. Not supported inside overlay. Optionally, control the background scene by passing a text prompt: * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. * * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill|AI Transformations - Generative Fill Background} @@ -222,14 +222,14 @@ export interface Transformation { grayscale?: true; /** - * Upscales images beyond their original dimensions using AI. + * Upscales images beyond their original dimensions using AI. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} */ aiUpscale?: true /** - * Performs AI-based retouching to improve faces or product shots. + * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} */ @@ -237,7 +237,7 @@ export interface Transformation { /** * Generates a variation of an image using AI. This produces a new image with slight variations from the original, - * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. + * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} */ @@ -246,7 +246,8 @@ export interface Transformation { /** * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). - * Pass true for the default drop shadow, or provide a string for a custom drop shadow. + * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. + * Supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow|AI Transformations - Drop Shadow} */ @@ -255,6 +256,7 @@ export interface Transformation { /** * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg|AI Transformations - Change Background} */ @@ -262,6 +264,7 @@ export interface Transformation { /** * Applies ImageKit’s in-house background removal. + * Supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} */ @@ -270,6 +273,7 @@ export interface Transformation { /** * Uses third-party background removal. * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. + * Supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg|AI Transformations - External Background Removal} */ @@ -285,7 +289,7 @@ export interface Transformation { /** * Adds a shadow beneath solid objects in an image with a transparent background. * For AI-based drop shadows, refer to aiDropShadow. - * Pass true for a default shadow, or provide a string for a custom shadow. + * Pass `true` for a default shadow, or provide a string for a custom shadow. * * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow|Effects and Enhancements - Shadow} */ @@ -293,7 +297,7 @@ export interface Transformation { /** * Sharpens the input image, highlighting edges and finer details. - * Pass true for default sharpening, or provide a numeric value for custom sharpening. + * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. * * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} */ @@ -301,14 +305,14 @@ export interface Transformation { /** * Applies Unsharp Masking (USM), an image sharpening technique. - * Pass true for a default unsharp mask, or provide a string for a custom unsharp mask. + * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. * * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} */ unsharpMask?: true | string; /** - * Creates a linear gradient with two colors. Pass true for a default gradient, or provide a string for a custom gradient. + * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. * * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} */ @@ -428,6 +432,31 @@ export interface Transformation { overlay?: Overlay; } +export type Overlay = + | TextOverlay + | ImageOverlay + | VideoOverlay + | SubtitleOverlay + | SolidColorOverlay + +export interface BaseOverlay { + /** + * Specifies the overlay's position relative to the parent asset. + * Accepts a JSON object with `x` and `y` (or `focus`) properties. + * + * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} + */ + position?: OverlayPosition; + + /** + * Specifies timing information for the overlay (only applicable if the base asset is a video). + * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. + * + * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} + */ + timing?: OverlayTiming; +} + export interface OverlayPosition { /** * Specifies the x-coordinate of the top-left corner of the base asset where the overlay's top-left corner will be positioned. @@ -485,32 +514,6 @@ export interface OverlayTiming { end?: number | string; } - -interface BaseOverlay { - /** - * Specifies the overlay's position relative to the parent asset. - * Accepts a JSON object with `x` and `y` (or `focus`) properties. - * - * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} - */ - position?: OverlayPosition; - - /** - * Specifies timing information for the overlay (only applicable if the base asset is a video). - * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. - * - * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} - */ - timing?: OverlayTiming; - - /** - * An array of transformations to be applied to the overlay. - * The supported transformations depend on the type of the base and overlay asset. - */ - transformations?: Transformation[]; -} - - export interface TextOverlay extends BaseOverlay { type: "text"; @@ -519,6 +522,11 @@ export interface TextOverlay extends BaseOverlay { * The SDK automatically handles special characters and URL encoding. */ text: string; + + /** + * Control styling of the text overlay. + */ + transformations?: TextOverlayTransformation[]; } export interface ImageOverlay extends BaseOverlay { @@ -528,6 +536,13 @@ export interface ImageOverlay extends BaseOverlay { * Specifies the relative path to the image used as an overlay. */ input: string; + + /** + * List of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. + * + * {@link https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay|Video} + */ + transformations?: Transformation[]; } export interface VideoOverlay extends BaseOverlay { @@ -536,6 +551,13 @@ export interface VideoOverlay extends BaseOverlay { * Specifies the relative path to the video used as an overlay. */ input: string; + + /** + * List of transformations to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. + * + * {@link https://imagekit.io/docs/video-transformation|Video Transformations} + */ + transformations?: Transformation[]; } export interface SubtitleOverlay extends BaseOverlay { @@ -544,6 +566,13 @@ export interface SubtitleOverlay extends BaseOverlay { * Specifies the relative path to the subtitle file used as an overlay. */ input: string; + + /** + * Control styling of the subtitle. + * + * {@link https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer|Styling subtitles} + */ + transformations?: SubtitleOverlayTransformation[]; } export interface SolidColorOverlay extends BaseOverlay { @@ -553,11 +582,131 @@ export interface SolidColorOverlay extends BaseOverlay { * If an 8-character value is provided, the last two characters represent the opacity level (from `00` for 0.00 to `99` for 0.99). */ color: string; + + /** + * Control width and height of the solid color overlay. Supported transformations depend on the base/parent asset. + * + * {@link https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay|Video} + */ + transformations?: SolidColorOverlayTransformation[]; } -export type Overlay = - | TextOverlay - | ImageOverlay - | VideoOverlay - | SubtitleOverlay - | SolidColorOverlay; +export type TextOverlayTransformation = { + /** + * Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. Useful when used in conjunction with the `backgroundColor`. + */ + width?: number | string; + + /** + * Specifies the font size of the overlaid text. Accepts a numeric value, a percentage, or an arithmetic expression. + */ + fontSize?: number | string; + + /** + * Specifies the font family of the overlaid text. Choose from the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) or use a [custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). + */ + fontFamily?: string; + + /** + * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + */ + fontColor?: string; + + /** + * Specifies the inner alignment of the text when width is more than the text length. + * Supported values: `left`, `right`, and `center` (default). + */ + innerAlignment?: "left" | "right" | "center"; + + /** + * Specifies the padding around the overlaid text. + * Can be provided as a single positive integer or multiple values separated by underscores (following CSS shorthand order). + * Arithmetic expressions are also accepted. + */ + padding?: number | string; + + /** + * Specifies the transparency level of the text overlay. Accepts integers from `1` to `9`. + */ + alpha?: number; + + /** + * Specifies the typography style of the text. + * Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. + */ + typography?: "b" | "i" | "b_i"; + + /** + * Specifies the background color of the text overlay. + * Accepts an RGB hex code, an RGBA code, or a color name. + */ + background?: string; + + /** + * Specifies the corner radius of the text overlay. + * Set to `max` to achieve a circular or oval shape. + */ + radius?: number | "max"; + + /** + * Specifies the rotation angle of the text overlay. + * Accepts a numeric value for clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. + */ + rotation?: number | string; + + /** + * Flip/mirror the text horizontally, vertically, or in both directions. + * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. + */ + flip?: "h" | "v" | "h_v" | "v_h"; + + /** + * Specifies the line height for multi-line text overlays. It will come into effect only if the text wraps over multiple lines. + * Accepts either an integer value or an arithmetic expression. + */ + lineHeight?: number | string; +} + +export type SubtitleOverlayTransformation = { + /** + * Specifies the subtitle background color using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). + */ + background?: string; + /** + * Sets the font size of subtitle text. + */ + fontSize?: number | string; + /** + * Sets the font family of subtitle text. + * Refer to the [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) in the ImageKit transformations guide. + */ + fontFamily?: string; + /** + * Sets the font color of the subtitle text using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). + */ + color?: string; + /** + * Sets the typography style of the subtitle text. + * Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. + */ + typography?: "b" | "i" | "b_i"; + /** + * Sets the font outline of the subtitle text. + * Requires the outline width (an integer) and the outline color (as an RGB color code, RGBA color code, or standard web color name) separated by an underscore. + * Examples: `2_blue`, `2_A1CCDD`, or `2_A1CCDD50`. + */ + fontOutline?: string; + /** + * Sets the font shadow for the subtitle text. + * Requires the shadow color (as an RGB color code, RGBA color code, or standard web color name) and the shadow indent (an integer) separated by an underscore. + * Examples: `blue_2`, `A1CCDD_3`, or `A1CCDD50_3`. + */ + fontShadow?: string; +} + +export type SolidColorOverlayTransformation = Pick & { + /** + * Specifies the transparency level of the overlaid solid color layer. Supports integers from `1` to `9`. + */ + alpha?: number; +} From 82b5f63c946c40ef6d3bc8c5f26808a3494663a4 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 22 Mar 2025 19:26:43 +0700 Subject: [PATCH 077/166] test cases and logic for overlay handling --- src/constants/supportedTransforms.ts | 14 ++ src/interfaces/Transformation.ts | 14 +- src/url/builder.ts | 93 +++++++++++- src/utils/transformation.ts | 23 ++- test/url-generation/overlay.js | 212 +++++++++++++++++++++++++++ 5 files changed, 339 insertions(+), 17 deletions(-) create mode 100644 test/url-generation/overlay.js diff --git a/src/constants/supportedTransforms.ts b/src/constants/supportedTransforms.ts index bbc560f..52a64c3 100644 --- a/src/constants/supportedTransforms.ts +++ b/src/constants/supportedTransforms.ts @@ -66,6 +66,20 @@ export const supportedTransforms: { [key: string]: string } = { zoom: "z", page: "pg", + // Text overlay transformations which are not defined yet + fontSize: "fs", + fontFamily: "ff", + fontColor: "co", + innerAlignment: "ia", + padding: "pa", + alpha: "al", + typography: "tg", + lineHeight: "lh", + + // Subtitles transformations which are not defined + fontOutline: "fol", + fontShadow: "fsh", + // Raw pass-through raw: "raw", }; diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 4909bf5..7090985 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -526,7 +526,7 @@ export interface TextOverlay extends BaseOverlay { /** * Control styling of the text overlay. */ - transformations?: TextOverlayTransformation[]; + transformation?: TextOverlayTransformation[]; } export interface ImageOverlay extends BaseOverlay { @@ -538,11 +538,11 @@ export interface ImageOverlay extends BaseOverlay { input: string; /** - * List of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. + * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. * * {@link https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay|Video} */ - transformations?: Transformation[]; + transformation?: Transformation[]; } export interface VideoOverlay extends BaseOverlay { @@ -553,11 +553,11 @@ export interface VideoOverlay extends BaseOverlay { input: string; /** - * List of transformations to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. + * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. * * {@link https://imagekit.io/docs/video-transformation|Video Transformations} */ - transformations?: Transformation[]; + transformation?: Transformation[]; } export interface SubtitleOverlay extends BaseOverlay { @@ -572,7 +572,7 @@ export interface SubtitleOverlay extends BaseOverlay { * * {@link https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer|Styling subtitles} */ - transformations?: SubtitleOverlayTransformation[]; + transformation?: SubtitleOverlayTransformation[]; } export interface SolidColorOverlay extends BaseOverlay { @@ -588,7 +588,7 @@ export interface SolidColorOverlay extends BaseOverlay { * * {@link https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay|Video} */ - transformations?: SolidColorOverlayTransformation[]; + transformation?: SolidColorOverlayTransformation[]; } export type TextOverlayTransformation = { diff --git a/src/url/builder.ts b/src/url/builder.ts index 29e13f8..226d969 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -1,6 +1,7 @@ import { ImageKitOptions, UrlOptions } from "../interfaces"; import { Transformation } from "../interfaces/Transformation"; import transformationUtils from "../utils/transformation"; +import { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; function removeTrailingSlash(str: string) { @@ -73,20 +74,106 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { return urlObj.href; }; + +function processOverlay(overlay: Transformation["overlay"]): string | undefined { + const entries = []; + if (!overlay) { + return; + } + const { type, position = {}, timing = {}, transformation = [] } = overlay; + + if (!type) { + throw new Error("Overlay type is required"); + } + + switch (type) { + case "text": + entries.push("l-text"); + if (overlay.text) { + entries.push(`ie-${encodeURIComponent(safeBtoa(overlay.text))}`); + } + break; + case "image": + entries.push("l-image"); + if (overlay.input) { + entries.push(`i-${overlay.input}`); + } + break; + case "video": + entries.push("l-video"); + if (overlay.input) { + entries.push(`i-${overlay.input}`); + } + break; + case "subtitle": + entries.push("l-subtitle"); + if (overlay.input) { + entries.push(`i-${overlay.input}`); + } + break; + case "solidColor": + entries.push("l-image"); + entries.push(`i-ik_canvas`); + if (overlay.color) { + entries.push(`bg-${overlay.color}`); + } + break; + } + + const { x, y, focus } = position; + if (x) { + entries.push(`lxo-${x}`); + } + if (y) { + entries.push(`lyo-${y}`); + } + if (focus) { + entries.push(`lfo-${focus}`); + } + + const { start, end, duration } = timing; + + if (start) { + entries.push(`lso-${start}`); + } + if (end) { + entries.push(`leo-${end}`); + } + if (duration) { + entries.push(`ldu-${duration}`); + } + + const transformationString = constructTransformationString(transformation); + + if (transformationString && transformationString.trim() !== "") entries.push(transformationString); + + entries.push("l-end"); + + return entries.join(transformationUtils.getTransformDelimiter()); +} + function constructTransformationString(transformation: Transformation[] | undefined) { if (!Array.isArray(transformation)) { return ""; } - var parsedTransforms = []; + var parsedTransforms: string[] = []; for (var i = 0, l = transformation.length; i < l; i++) { - var parsedTransformStep = []; + var parsedTransformStep: string[] = []; for (var key in transformation[i]) { let value = transformation[i][key as keyof Transformation]; if (value === undefined || value === null) { continue; } + if (key === "overlay" && typeof value === "object") { + var rawString = processOverlay(value as Transformation["overlay"]); + if (rawString) { + parsedTransformStep.push(rawString); + continue; + } + } + var transformKey = transformationUtils.getTransformKey(key); if (!transformKey) { transformKey = key; @@ -111,7 +198,7 @@ function constructTransformationString(transformation: Transformation[] | undefi ) { parsedTransformStep.push(transformKey); } else if (key === "raw") { - parsedTransformStep.push(transformation[i][key]); + parsedTransformStep.push(transformation[i][key] as string); } else { if (transformKey === "di") { value = removeTrailingSlash(removeLeadingSlash(value as string || "")); diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index ef4ec26..c107a69 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -1,12 +1,12 @@ import supportedTransforms from "../constants/supportedTransforms"; import { ImageKitOptions, TransformationPosition } from "../interfaces"; -const DEFAULT_TRANSFORMATION_POSITION : TransformationPosition = "path"; -const QUERY_TRANSFORMATION_POSITION : TransformationPosition = "query"; +const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = "path"; +const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; const VALID_TRANSFORMATION_POSITIONS = [DEFAULT_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; -const CHAIN_TRANSFORM_DELIMITER : string = ":"; -const TRANSFORM_DELIMITER : string = ","; -const TRANSFORM_KEY_VALUE_DELIMITER : string = "-"; +const CHAIN_TRANSFORM_DELIMITER: string = ":"; +const TRANSFORM_DELIMITER: string = ","; +const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; export default { getDefault: (): TransformationPosition => { @@ -15,8 +15,8 @@ export default { addAsQueryParameter: (options: ImageKitOptions) => { return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; }, - validParameters: (options: ImageKitOptions) => { - if(typeof options.transformationPosition == "undefined") return false; + validParameters: (options: ImageKitOptions) => { + if (typeof options.transformationPosition == "undefined") return false; return VALID_TRANSFORMATION_POSITIONS.indexOf(options.transformationPosition) != -1; }, getTransformKey: function (transform: string) { @@ -33,4 +33,13 @@ export default { getTransformKeyValueDelimiter: function () { return TRANSFORM_KEY_VALUE_DELIMITER; } +} + +export const safeBtoa = function (str: string): string { + if (typeof btoa !== "undefined") { + return btoa(str); + } else { + // Node fallback + return Buffer.from(str, "utf8").toString("base64"); + } } \ No newline at end of file diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js new file mode 100644 index 0000000..c4d71bc --- /dev/null +++ b/test/url-generation/overlay.js @@ -0,0 +1,212 @@ +const chai = require("chai"); +const expect = chai.expect; +const initializationParams = require("../data").initializationParams; +import ImageKit from "../../src/index"; +import { safeBtoa } from "../../src/utils/transformation"; +describe.only("Comprehensive Overlay Transformation Cases", function () { + const imagekit = new ImageKit(initializationParams); + + it('simple text overlay', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "text", + text: "Minimal Text", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Minimal Text"))},l-end/base-image.jpg`); + }); + + it('simple image overlay', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", + input: "logo.png", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,l-end/base-image.jpg`); + }); + + it('simple video overlay', function () { + const url = imagekit.url({ + path: "/base-video.mp4", + transformation: [{ + overlay: { + type: "video", + input: "play-pause-loop.mp4", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-video,i-play-pause-loop.mp4,l-end/base-video.mp4`); + }); + + it("simple subtitle overlay", function () { + const url = imagekit.url({ + path: "/base-video.mp4", + transformation: [{ + overlay: { + type: "subtitle", + input: "subtitle.srt", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-subtitle,i-subtitle.srt,l-end/base-video.mp4`); + }); + + it("simple solid color overlay", function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "solidColor", + color: "FF0000", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-ik_canvas,bg-FF0000,l-end/base-image.jpg`); + }); + + it('All combined', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [ + { + // Text overlay + overlay: { + type: "text", + text: "Every thing", + position: { + x: "10", + y: "20", + focus: "center" + }, + timing: { + start: 5, + duration: "10", + end: 15 + }, + transformations: [{ + width: "bw_mul_0.5", + fontSize: 20, + fontFamily: "Arial", + fontColor: "0000ff", + innerAlignment: "left", + padding: 5, + alpha: 7, + typography: "b", + background: "red", + radius: 10, + rotation: "N45", + flip: "h", + lineHeight: 20 + }] + } + }, + { + // Image overlay + overlay: { + type: "image", + input: "logo.png", + position: { + x: "10", + y: "20", + focus: "center" + }, + timing: { + start: 5, + duration: "10", + end: 15 + }, + transformations: [ + { + width: "bw_mul_0.5", + height: "bh_mul_0.5", + rotation: "N45", + flip: "h", + overlay: { + type: "text", + text: "Nested text overlay", + } + } + ] + } + }, + { + // Video overlay. Just for url generation testing, you can't overlay a video on an image. + overlay: { + type: "video", + input: "play-pause-loop.mp4", + position: { + x: "10", + y: "20", + focus: "center" + }, + timing: { + start: 5, + duration: "10", + end: 15 + }, + transformations: [{ + width: "bw_mul_0.5", + height: "bh_mul_0.5", + rotation: "N45", + flip: "h", + }] + } + }, + { + // Subtitle overlay. Just for url generation testing, you can't overlay a subtitle on an image. + overlay: { + type: "subtitle", + input: "subtitle.srt", + position: { + x: "10", + y: "20", + focus: "center" + }, + timing: { + start: 5, + duration: "10", + end: 15 + }, + transformations: [{ + width: "bw_mul_0.5", + height: "bh_mul_0.5", + rotation: "N45", + flip: "h", + }] + } + }, + { + // Solid color overlay + overlay: { + type: "solidColor", + color: "FF0000", + position: { + x: "10", + y: "20", + focus: "center" + }, + timing: { + start: 5, + duration: "10", + end: 15 + }, + transformations: [{ + width: "bw_mul_0.5", + height: "bh_mul_0.5", + rotation: "N45", + flip: "h", + }] + } + } + ] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Every thing"))},lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,fs-20,ff-Arial,fc-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,ie-${encodeURIComponent(safeBtoa("Nested text overlay"))},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,l-end/base-image.jpg`); + }); +}); From 6f93838585ccf737fa5b200119106c279cc232dd Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 22 Mar 2025 19:35:14 +0700 Subject: [PATCH 078/166] fix test case --- test/url-generation/overlay.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index c4d71bc..3328830 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -3,10 +3,10 @@ const expect = chai.expect; const initializationParams = require("../data").initializationParams; import ImageKit from "../../src/index"; import { safeBtoa } from "../../src/utils/transformation"; -describe.only("Comprehensive Overlay Transformation Cases", function () { +describe("Overlay Transformation Test Cases", function () { const imagekit = new ImageKit(initializationParams); - it('simple text overlay', function () { + it('Text overlay generates correct URL with encoded overlay text', function () { const url = imagekit.url({ path: "/base-image.jpg", transformation: [{ @@ -19,7 +19,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Minimal Text"))},l-end/base-image.jpg`); }); - it('simple image overlay', function () { + it('Image overlay generates correct URL with input logo.png', function () { const url = imagekit.url({ path: "/base-image.jpg", transformation: [{ @@ -32,7 +32,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,l-end/base-image.jpg`); }); - it('simple video overlay', function () { + it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { const url = imagekit.url({ path: "/base-video.mp4", transformation: [{ @@ -45,7 +45,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-video,i-play-pause-loop.mp4,l-end/base-video.mp4`); }); - it("simple subtitle overlay", function () { + it("Subtitle overlay generates correct URL with input subtitle.srt", function () { const url = imagekit.url({ path: "/base-video.mp4", transformation: [{ @@ -58,7 +58,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-subtitle,i-subtitle.srt,l-end/base-video.mp4`); }); - it("simple solid color overlay", function () { + it("Solid color overlay generates correct URL with background color FF0000", function () { const url = imagekit.url({ path: "/base-image.jpg", transformation: [{ @@ -71,7 +71,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-ik_canvas,bg-FF0000,l-end/base-image.jpg`); }); - it('All combined', function () { + it('Combined overlay transformations generate correct URL including nested overlays', function () { const url = imagekit.url({ path: "/base-image.jpg", transformation: [ @@ -90,7 +90,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { duration: "10", end: 15 }, - transformations: [{ + transformation: [{ width: "bw_mul_0.5", fontSize: 20, fontFamily: "Arial", @@ -122,7 +122,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { duration: "10", end: 15 }, - transformations: [ + transformation: [ { width: "bw_mul_0.5", height: "bh_mul_0.5", @@ -197,7 +197,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { duration: "10", end: 15 }, - transformations: [{ + transformation: [{ width: "bw_mul_0.5", height: "bh_mul_0.5", rotation: "N45", @@ -207,6 +207,7 @@ describe.only("Comprehensive Overlay Transformation Cases", function () { } ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Every thing"))},lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,fs-20,ff-Arial,fc-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,ie-${encodeURIComponent(safeBtoa("Nested text overlay"))},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,ldu-10,leo-15,w-bw_mul_0.5,l-end/base-image.jpg`); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Every thing"))},lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,ie-${encodeURIComponent(safeBtoa("Nested text overlay"))},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) }); }); From 2d95621d72e96c1af7deebff7ca3c228d4e5a303 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 22 Mar 2025 19:44:30 +0700 Subject: [PATCH 079/166] fix build --- src/url/builder.ts | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/url/builder.ts b/src/url/builder.ts index 226d969..1e5432b 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -1,7 +1,6 @@ import { ImageKitOptions, UrlOptions } from "../interfaces"; -import { Transformation } from "../interfaces/Transformation"; -import transformationUtils from "../utils/transformation"; -import { safeBtoa } from "../utils/transformation"; +import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; +import transformationUtils, { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; function removeTrailingSlash(str: string) { @@ -89,33 +88,48 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined switch (type) { case "text": entries.push("l-text"); - if (overlay.text) { - entries.push(`ie-${encodeURIComponent(safeBtoa(overlay.text))}`); + { + const textOverlay = overlay as TextOverlay; + if (textOverlay.text) { + entries.push(`ie-${encodeURIComponent(safeBtoa(textOverlay.text))}`); + } } break; case "image": entries.push("l-image"); - if (overlay.input) { - entries.push(`i-${overlay.input}`); + { + const imageOverlay = overlay as ImageOverlay; + if (imageOverlay.input) { + entries.push(`i-${imageOverlay.input}`); + } } break; case "video": entries.push("l-video"); - if (overlay.input) { - entries.push(`i-${overlay.input}`); + { + const videoOverlay = overlay as VideoOverlay; + if (videoOverlay.input) { + entries.push(`i-${videoOverlay.input}`); + } } break; case "subtitle": entries.push("l-subtitle"); - if (overlay.input) { - entries.push(`i-${overlay.input}`); + { + const subtitleOverlay = overlay as SubtitleOverlay; + if (subtitleOverlay.input) { + entries.push(`i-${subtitleOverlay.input}`); + } } break; case "solidColor": entries.push("l-image"); entries.push(`i-ik_canvas`); - if (overlay.color) { - entries.push(`bg-${overlay.color}`); + { + const solidColorOverlay = overlay as SolidColorOverlay; + if (solidColorOverlay.color) { + entries.push(`bg-${solidColorOverlay.color}`); + } } break; } From 397ab3171bc29321ee3d7e77c3c6c8219017b039 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 22 Mar 2025 19:52:00 +0700 Subject: [PATCH 080/166] fix: handle missing values in overlay processing to prevent invalid URLs --- src/url/builder.ts | 26 ++++++++++----- test/url-generation/overlay.js | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/url/builder.ts b/src/url/builder.ts index 1e5432b..d3be494 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -86,14 +86,14 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined } switch (type) { - case "text": - entries.push("l-text"); - { - const textOverlay = overlay as TextOverlay; - if (textOverlay.text) { - entries.push(`ie-${encodeURIComponent(safeBtoa(textOverlay.text))}`); - } + case "text": { + const textOverlay = overlay as TextOverlay; + if (!textOverlay.text) { + return; } + entries.push("l-text"); + entries.push(`ie-${encodeURIComponent(safeBtoa(textOverlay.text))}`); + } break; case "image": entries.push("l-image"); @@ -101,6 +101,8 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined const imageOverlay = overlay as ImageOverlay; if (imageOverlay.input) { entries.push(`i-${imageOverlay.input}`); + } else { + return; } } break; @@ -110,6 +112,8 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined const videoOverlay = overlay as VideoOverlay; if (videoOverlay.input) { entries.push(`i-${videoOverlay.input}`); + } else { + return; } } break; @@ -119,6 +123,8 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined const subtitleOverlay = overlay as SubtitleOverlay; if (subtitleOverlay.input) { entries.push(`i-${subtitleOverlay.input}`); + } else { + return; } } break; @@ -129,6 +135,8 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined const solidColorOverlay = overlay as SolidColorOverlay; if (solidColorOverlay.color) { entries.push(`bg-${solidColorOverlay.color}`); + } else { + return; } } break; @@ -182,10 +190,10 @@ function constructTransformationString(transformation: Transformation[] | undefi if (key === "overlay" && typeof value === "object") { var rawString = processOverlay(value as Transformation["overlay"]); - if (rawString) { + if (rawString && rawString.trim() !== "") { parsedTransformStep.push(rawString); - continue; } + continue; // Always continue as overlay is processed. } var transformKey = transformationUtils.getTransformKey(key); diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 3328830..3456041 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -6,6 +6,66 @@ import { safeBtoa } from "../../src/utils/transformation"; describe("Overlay Transformation Test Cases", function () { const imagekit = new ImageKit(initializationParams); + it('Ignore invalid values if text is missing', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "text" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "video" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if input', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "subtitle" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + + it('Ignore invalid values if color is missing', function () { + const url = imagekit.url({ + path: "/base-image.jpg", + transformation: [{ + overlay: { + type: "solidColor" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + it('Text overlay generates correct URL with encoded overlay text', function () { const url = imagekit.url({ path: "/base-image.jpg", From bc036204e89785e7792397ebb85a4bd0887222eb Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 22 Mar 2025 20:01:57 +0700 Subject: [PATCH 081/166] Add proper overlay examples --- README.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 103 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2b06eea..f9c0bdf 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,8 @@ The SDK’s `.url()` method enables you to generate optimized image and video UR path: "/default-image.jpg", urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", transformation: [{ - "height": "300", - "width": "400" + height: 300, + width: 400 }] }); ``` @@ -103,8 +103,8 @@ The SDK’s `.url()` method enables you to generate optimized image and video UR var imageURL = imagekit.url({ src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", transformation: [{ - "height": "300", - "width": "400" + height: 300, + width: 400 }] }); ``` @@ -121,10 +121,10 @@ Apply multiple transformations by passing an array: var imageURL = imagekit.url({ path: "/default-image.jpg", transformation: [{ - "height": "300", - "width": "400" + height: 300, + width: 400 }, { - "rotation": 90 + rotation: 90 }], transformationPosition: "query" // Use query parameter for transformations }); @@ -140,20 +140,106 @@ https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300%2Cw-400%3Art- var imageURL = imagekit.url({ src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", transformation: [{ - "width": 400, - "height": 300, - "raw": "l-text,i-Imagekit,fs-50,l-end" + width: 400, + height: 300, + overlay: { + text: "Imagekit", + fontSize: 50, + color: "red", + position: { + x: 10, + y: 20 + } + } }] }); ``` + *Image Overlay Example:* + ```js var imageURL = imagekit.url({ src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", transformation: [{ - "width": 400, - "height": 300, - "raw": "l-image,i-default-image.jpg,w-100,b-10_CDDC39,l-end" + width: 400, + height: 300, + overlay: { + type: "image", + input: "logo.png", + transformation: [{ + width: 100, + border: "10_CDDC39" + }], + position: { + focus: "top_left" + } + } + }] +}); +``` + +*Video Overlay Example:* + +```js +var videoOverlayURL = imagekit.url({ + src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", + transformation: [{ + overlay: { + type: "video", + input: "overlay-video.mp4", + position: { + x: "10", + y: "20" + }, + timing: { + start: 5, + duration: 10 + } + } + }] +}); +``` + +*Subtitle Overlay Example:* + +```js +var subtitleOverlayURL = imagekit.url({ + src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", + transformation: [{ + overlay: { + type: "subtitle", + input: "subtitle.vtt", + transformation: [{ + fontSize: 16, + fontFamily: "Arial" + }], + position: { + focus: "bottom" + }, + timing: { + start: 0, + duration: 5 + } + } + }] +}); +``` + +*Solid Color Overlay Example:* +```js +var solidColorOverlayURL = imagekit.url({ + src: "https://ik.imagekit.io/your_imagekit_id/base-image.jpg", + transformation: [{ + overlay: { + type: "solidColor", + color: "FF0000", + transformation: [{ + width: 100, + height: 50, + alpha: 5 + }], + position: { x: 20, y: 20 } + } }] }); ``` @@ -192,9 +278,9 @@ var dropShadowURL = imagekit.url({ 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" + width: "iw_div_4", + height: "ih_div_2", + border: "cw_mul_0.05_yellow" }] }); ``` @@ -269,7 +355,7 @@ For example: var imageURL = imagekit.url({ path: "/test_path.jpg", transformation: [{ - "newparam": "cool" + newparam: "cool" }] }); // Generated URL: https://ik.imagekit.io/test_url_endpoint/tr:newparam-cool/test_path.jpg From 57e0066bf65319727ee4e61034b7ba887dc4bfba Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 12:30:23 +0530 Subject: [PATCH 082/166] feat: add encoding options for overlay input paths and texts --- src/interfaces/Transformation.ts | 50 +++++++++++++++++++++ src/url/builder.ts | 43 ++++++++++++++++-- src/utils/transformation.ts | 2 +- test/url-generation/overlay.js | 77 +++++++++++++++++++++++++++++++- 4 files changed, 165 insertions(+), 7 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 7090985..ff1c9c6 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -523,6 +523,17 @@ export interface TextOverlay extends BaseOverlay { */ text: string; + /** + * Specifies how the overlay input text should be encoded. The default is `auto`, which means the SDK will initially treat the text as plain text to improve URL readability. If the text contains special characters, the SDK will automatically switch to `base64` encoding. + * + * You can also explicitly set the encoding to either `plain` or `base64`. + * + * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * + * * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. + */ + encoding: "auto" | "plain" | "base64"; + /** * Control styling of the text overlay. */ @@ -537,6 +548,19 @@ export interface ImageOverlay extends BaseOverlay { */ input: string; + /** + * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. + * + * You can also explicitly set the encoding to either `plain` or `base64`. + * + * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * + * * Regardless of the encoding method: + * - Leading and trailing slashes are removed. + * - Any remaining slashes within the path are replaced with `@@` when using plain text. + */ + encoding: "auto" | "plain" | "base64"; + /** * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. * @@ -552,6 +576,19 @@ export interface VideoOverlay extends BaseOverlay { */ input: string; + /** + * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. + * + * You can also explicitly set the encoding to either `plain` or `base64`. + * + * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * + * * Regardless of the encoding method: + * - Leading and trailing slashes are removed. + * - Any remaining slashes within the path are replaced with `@@` when using plain text. + */ + encoding: "auto" | "plain" | "base64"; + /** * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. * @@ -567,6 +604,19 @@ export interface SubtitleOverlay extends BaseOverlay { */ input: string; + /** + * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. + * + * You can also explicitly set the encoding to either `plain` or `base64`. + * + * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * + * * Regardless of the encoding method: + * - Leading and trailing slashes are removed. + * - Any remaining slashes within the path are replaced with `@@` when using plain text. + */ + encoding: "auto" | "plain" | "base64"; + /** * Control styling of the subtitle. * diff --git a/src/url/builder.ts b/src/url/builder.ts index d3be494..d014230 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -2,6 +2,8 @@ import { ImageKitOptions, UrlOptions } from "../interfaces"; import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; import transformationUtils, { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; +const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._,/ ]*$') +const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._, ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. function removeTrailingSlash(str: string) { if (typeof str == "string" && str[str.length - 1] == "/") { @@ -73,6 +75,34 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { return urlObj.href; }; +function processInputPath(str: string, enccoding: string): string { + // Remove leading and trailing slashes + str = removeTrailingSlash(removeLeadingSlash(str)); + if(enccoding === "plain") { + return `i-${str.replace(/\//g, "@@")}`; + } + if(enccoding === "base64") { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } + if (SIMPLE_OVERLAY_PATH_REGEX.test(str)) { + return `i-${str.replace(/\//g, "@@")}`; + } else { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } +} + +function processText(str: string, enccoding: TextOverlay["encoding"]): string { + if (enccoding === "plain") { + return `i-${encodeURIComponent(str)}`; + } + if (enccoding === "base64") { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } + if (SIMPLE_OVERLAY_TEXT_REGEX.test(str)) { + return `i-${encodeURIComponent(str)}`; + } + return `ie-${encodeURIComponent(safeBtoa(str))}`; +} function processOverlay(overlay: Transformation["overlay"]): string | undefined { const entries = []; @@ -91,16 +121,19 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined if (!textOverlay.text) { return; } + const enccoding = textOverlay.encoding || "auto"; + entries.push("l-text"); - entries.push(`ie-${encodeURIComponent(safeBtoa(textOverlay.text))}`); + entries.push(processText(textOverlay.text, enccoding)); } break; case "image": entries.push("l-image"); { const imageOverlay = overlay as ImageOverlay; + const enccoding = imageOverlay.encoding || "auto"; if (imageOverlay.input) { - entries.push(`i-${imageOverlay.input}`); + entries.push(processInputPath(imageOverlay.input, enccoding)); } else { return; } @@ -110,8 +143,9 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined entries.push("l-video"); { const videoOverlay = overlay as VideoOverlay; + const enccoding = videoOverlay.encoding || "auto"; if (videoOverlay.input) { - entries.push(`i-${videoOverlay.input}`); + entries.push(processInputPath(videoOverlay.input, enccoding)); } else { return; } @@ -121,8 +155,9 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined entries.push("l-subtitle"); { const subtitleOverlay = overlay as SubtitleOverlay; + const enccoding = subtitleOverlay.encoding || "auto"; if (subtitleOverlay.input) { - entries.push(`i-${subtitleOverlay.input}`); + entries.push(processInputPath(subtitleOverlay.input, enccoding)); } else { return; } diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index c107a69..0ef2c25 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -36,7 +36,7 @@ export default { } export const safeBtoa = function (str: string): string { - if (typeof btoa !== "undefined") { + if (typeof window !== "undefined") { return btoa(str); } else { // Node fallback diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 3456041..b4367b8 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -76,7 +76,7 @@ describe("Overlay Transformation Test Cases", function () { } }] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Minimal Text"))},l-end/base-image.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Minimal Text")},l-end/base-image.jpg`); }); it('Image overlay generates correct URL with input logo.png', function () { @@ -268,6 +268,79 @@ describe("Overlay Transformation Test Cases", function () { ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,ie-${encodeURIComponent(safeBtoa("Every thing"))},lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,ie-${encodeURIComponent(safeBtoa("Nested text overlay"))},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) }); }); + + +describe("Edge cases", function () { + const imagekit = new ImageKit({ + ...initializationParams, + urlEndpoint: "https://ik.imagekit.io/demo", // Using real url to test correctness quickly by clicking link + }); + + it('Nested simple path, should use i instead of ie, handle slash properly', function () { + const url = imagekit.url({ + path: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "image", + input: "/customer_logo/nykaa.png", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-image,i-customer_logo@@nykaa.png,l-end/medium_cafe_B1iTdD0C.jpg`); + }); + + it('Nested non-simple path, should use ie instead of i', function () { + const url = imagekit.url({ + path: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "image", + input: "/customer_logo/Ñykaa.png" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXJfbG9nby9OzIN5a2FhLnBuZw%3D%3D,l-end/medium_cafe_B1iTdD0C.jpg`); + }); + + it('Simple text overlay, should use i instead of ie', function () { + const url = imagekit.url({ + path: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", + text: "Manu", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-Manu,l-end/medium_cafe_B1iTdD0C.jpg`); + }); + + it('Simple text overlay with spaces and comma, should use i instead of ie', function () { + const url = imagekit.url({ + path: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", + text: "alnum123-._, ", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123-._, ")},l-end/medium_cafe_B1iTdD0C.jpg`); + }); + + it('Non simple text overlay, should use ie instead of i', function () { + const url = imagekit.url({ + path: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", + text: "Let's use ©, ®, ™, etc", + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,ie-TGV0J3MgdXNlIMKpLCDCriwg4oSiLCBldGM%3D,l-end/medium_cafe_B1iTdD0C.jpg`); + }); +}); \ No newline at end of file From 9275a502afdcc4d61a8b2afa91717bd4637b0a9c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 12:38:04 +0530 Subject: [PATCH 083/166] explict encoding test cases --- test/url-generation/overlay.js | 102 ++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index b4367b8..3a163f9 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -273,7 +273,7 @@ describe("Overlay Transformation Test Cases", function () { }); -describe("Edge cases", function () { +describe("Overlay encoding test cases", function () { const imagekit = new ImageKit({ ...initializationParams, urlEndpoint: "https://ik.imagekit.io/demo", // Using real url to test correctness quickly by clicking link @@ -343,4 +343,102 @@ describe("Edge cases", function () { }); expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,ie-TGV0J3MgdXNlIMKpLCDCriwg4oSiLCBldGM%3D,l-end/medium_cafe_B1iTdD0C.jpg`); }); -}); \ No newline at end of file + + it('Text overlay with explicit plain encoding', function () { + const url = imagekit.url({ + path: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", + text: "HelloWorld", + encoding: "plain" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-HelloWorld,l-end/sample.jpg`); + }); + + it('Text overlay with explicit base64 encoding', function () { + const url = imagekit.url({ + path: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", + text: "HelloWorld", + encoding: "base64" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,ie-${encodeURIComponent(safeBtoa("HelloWorld"))},l-end/sample.jpg`); + }); + + it('Image overlay with explicit plain encoding', function () { + const url = imagekit.url({ + path: "/sample.jpg", + transformation: [{ + overlay: { + type: "image", + input: "/customer/logo.png", + encoding: "plain" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-image,i-customer@@logo.png,l-end/sample.jpg`); + }); + + it('Image overlay with explicit base64 encoding', function () { + const url = imagekit.url({ + path: "/sample.jpg", + transformation: [{ + overlay: { + type: "image", + input: "/customer/logo.png", + encoding: "base64" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-image,ie-${encodeURIComponent(safeBtoa("customer/logo.png"))},l-end/sample.jpg`); + }); + + it('Video overlay with explicit base64 encoding', function () { + const url = imagekit.url({ + path: "/sample.mp4", + transformation: [{ + overlay: { + type: "video", + input: "/path/to/video.mp4", + encoding: "base64" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-video,ie-${encodeURIComponent(safeBtoa("path/to/video.mp4"))},l-end/sample.mp4`); + }); + + it('Subtitle overlay with explicit plain encoding', function () { + const url = imagekit.url({ + path: "/sample.mp4", + transformation: [{ + overlay: { + type: "subtitle", + input: "/sub.srt", + encoding: "plain" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitle,i-sub.srt,l-end/sample.mp4`); + }); + + it('Subtitle overlay with explicit base64 encoding', function () { + const url = imagekit.url({ + path: "/sample.mp4", + transformation: [{ + overlay: { + type: "subtitle", + input: "sub.srt", + encoding: "base64" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitle,ie-${encodeURIComponent(safeBtoa("sub.srt"))},l-end/sample.mp4`); + }); +}); From f9105100c878b9eb93e572c274c8d3acc0d25eb5 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 12:39:23 +0530 Subject: [PATCH 084/166] fix: correct formatting in documentation comments for overlay encoding methods --- src/interfaces/Transformation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index ff1c9c6..ead7b23 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -530,7 +530,7 @@ export interface TextOverlay extends BaseOverlay { * * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. * - * * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. + * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. */ encoding: "auto" | "plain" | "base64"; @@ -555,7 +555,7 @@ export interface ImageOverlay extends BaseOverlay { * * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. * - * * Regardless of the encoding method: + * Regardless of the encoding method: * - Leading and trailing slashes are removed. * - Any remaining slashes within the path are replaced with `@@` when using plain text. */ @@ -583,7 +583,7 @@ export interface VideoOverlay extends BaseOverlay { * * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. * - * * Regardless of the encoding method: + * Regardless of the encoding method: * - Leading and trailing slashes are removed. * - Any remaining slashes within the path are replaced with `@@` when using plain text. */ @@ -611,7 +611,7 @@ export interface SubtitleOverlay extends BaseOverlay { * * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. * - * * Regardless of the encoding method: + * Regardless of the encoding method: * - Leading and trailing slashes are removed. * - Any remaining slashes within the path are replaced with `@@` when using plain text. */ From 2485020cc0c035657a1b999eec21b61acb70436d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 12:55:37 +0530 Subject: [PATCH 085/166] docs: update README to include overlay options and configuration details --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index f9c0bdf..ef5a403 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ Lightweight JavaScript SDK for generating optimized URLs for images and videos, - [URL Generation](#url-generation) - [Basic URL Generation](#basic-url-generation) - [Advanced URL Generation Examples](#advanced-url-generation-examples) + - [Chained Transformations](#chained-transformations) + - [Overlays and Effects](#overlays-and-effects) + - [AI and Advanced Transformations](#ai-and-advanced-transformations) + - [Arithmetic Expressions in Transformations](#arithmetic-expressions-in-transformations) - [Supported Transformations](#supported-transformations) - [Handling Unsupported Transformations](#handling-unsupported-transformations) - [File Upload](#file-upload) @@ -244,6 +248,18 @@ var solidColorOverlayURL = imagekit.url({ }); ``` +**Overlay Options** + +The following table details the overlay configuration options as defined in the SDK. These options are passed in the overlay object and directly map to URL parameters: + +| option | Description | Example | +| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| encoding | Specifies how the overlay input is encoded. The default is "auto", meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to "plain" or "base64". | `encoding: "plain"` | +| position | Defines the overlay's placement relative to the parent asset. Accepts a JSON object with properties: `x` and `y` for coordinates (which can be arithmetic expressions) or a `focus` value such as "center", "top_left", etc. | `position: { x: 10, y: 20, focus: "center" }` | +| timing | When the base asset is video, specifies when the overlay appears. It accepts a JSON object with values for `start`, `duration`, and `end`. If both `duration` and `end` are provided, `duration` is ignored. | `timing: { start: 5, duration: 10, end: 15 }` | + +These options provide developers with fine-grained control over overlay transformations, ensuring that the generated URL accurately reflects the desired overlay configuration. + #### AI and Advanced Transformations *Background Removal:* ```js From c09f3d2d30a401f2d02b366a22fe28a08a59fb05 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 12:58:18 +0530 Subject: [PATCH 086/166] docs: improve formatting and clarity in overlay options section of README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef5a403..0ec9384 100644 --- a/README.md +++ b/README.md @@ -248,14 +248,14 @@ var solidColorOverlayURL = imagekit.url({ }); ``` -**Overlay Options** +##### Overlay Options The following table details the overlay configuration options as defined in the SDK. These options are passed in the overlay object and directly map to URL parameters: | option | Description | Example | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| encoding | Specifies how the overlay input is encoded. The default is "auto", meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to "plain" or "base64". | `encoding: "plain"` | -| position | Defines the overlay's placement relative to the parent asset. Accepts a JSON object with properties: `x` and `y` for coordinates (which can be arithmetic expressions) or a `focus` value such as "center", "top_left", etc. | `position: { x: 10, y: 20, focus: "center" }` | +| encoding | Specifies how the overlay input is encoded. The default is `auto`, meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to `plain` or `base64`. | `encoding: "plain"` | +| position | Defines the overlay's placement relative to the parent asset. Accepts a JSON object with properties: `x` and `y` for coordinates (which can be arithmetic expressions) or a `focus` value such as `center`, `top_left`, etc. | `position: { x: 10, y: 20, focus: "center" }` | | timing | When the base asset is video, specifies when the overlay appears. It accepts a JSON object with values for `start`, `duration`, and `end`. If both `duration` and `end` are provided, `duration` is ignored. | `timing: { start: 5, duration: 10, end: 15 }` | These options provide developers with fine-grained control over overlay transformations, ensuring that the generated URL accurately reflects the desired overlay configuration. From 42fbc20be939e521b97384ab101ae4d39a4e2f21 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 30 Mar 2025 15:21:49 +0530 Subject: [PATCH 087/166] docs: correct table header and update encoding example in overlay options section of README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ec9384..5c83fce 100644 --- a/README.md +++ b/README.md @@ -252,9 +252,9 @@ var solidColorOverlayURL = imagekit.url({ The following table details the overlay configuration options as defined in the SDK. These options are passed in the overlay object and directly map to URL parameters: -| option | Description | Example | +| Option | Description | Example | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| encoding | Specifies how the overlay input is encoded. The default is `auto`, meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to `plain` or `base64`. | `encoding: "plain"` | +| encoding | Specifies how the overlay input is encoded. The default is `auto`, meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to `plain` or `base64`. | `encoding: "base64"` | | position | Defines the overlay's placement relative to the parent asset. Accepts a JSON object with properties: `x` and `y` for coordinates (which can be arithmetic expressions) or a `focus` value such as `center`, `top_left`, etc. | `position: { x: 10, y: 20, focus: "center" }` | | timing | When the base asset is video, specifies when the overlay appears. It accepts a JSON object with values for `start`, `duration`, and `end`. If both `duration` and `end` are provided, `duration` is ignored. | `timing: { start: 5, duration: 10, end: 15 }` | From 45d8a871922ad0a08d4b67832e97d55f697ef274 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 14:35:25 +0530 Subject: [PATCH 088/166] docs: enhance encoding descriptions and clarify overlay input formats in Transformation interface --- README.md | 87 ++++++++++++++++++++++++++++---- src/interfaces/Transformation.ts | 50 +++++++++--------- 2 files changed, 101 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 5c83fce..e05f0aa 100644 --- a/README.md +++ b/README.md @@ -250,15 +250,84 @@ var solidColorOverlayURL = imagekit.url({ ##### Overlay Options -The following table details the overlay configuration options as defined in the SDK. These options are passed in the overlay object and directly map to URL parameters: - -| Option | Description | Example | -| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| encoding | Specifies how the overlay input is encoded. The default is `auto`, meaning the SDK automatically determines whether to use plain (`i-{input}`) or base64 (`ie-{base64_encoded_input}`) encoding based on the content. You can explicitly set it to `plain` or `base64`. | `encoding: "base64"` | -| position | Defines the overlay's placement relative to the parent asset. Accepts a JSON object with properties: `x` and `y` for coordinates (which can be arithmetic expressions) or a `focus` value such as `center`, `top_left`, etc. | `position: { x: 10, y: 20, focus: "center" }` | -| timing | When the base asset is video, specifies when the overlay appears. It accepts a JSON object with values for `start`, `duration`, and `end`. If both `duration` and `end` are provided, `duration` is ignored. | `timing: { start: 5, duration: 10, end: 15 }` | - -These options provide developers with fine-grained control over overlay transformations, ensuring that the generated URL accurately reflects the desired overlay configuration. +The table below outlines the available overlay configuration options: + +| Option | Description | Example | +| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| type | Specifies the type of overlay. Supported values: `text`, `image`, `video`, `subtitle`, `solidColor`. | `type: "text"` | +| text | (For text overlays) The text content to display. | `text: "ImageKit"` | +| input | (For image, video, or subtitle overlays) Relative path to the overlay asset. | `input: "logo.png"` or `input: "overlay-video.mp4"` | +| color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | +| encoding | Defines how the overlay input is encoded. Accepted values: `auto`, `plain`, `base64`. | `encoding: "auto"` | +| transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | +| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus` (e.g., `center`). | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | +| timing | (When base is a video) Defines when the overlay appears. Accepts an object with `start`, `duration`, and `end` properties (in seconds). | `timing: { start: 5, duration: 10 }` | + + +##### Encoding Options + +Overlay encoding options define how the overlay input is converted for URL construction. When set to `auto`, the SDK automatically determines whether to use plain text or Base64 encoding based on the input content. + +For text overlays: +- If `auto` is used, the SDK checks the text overlay input: if it is URL-safe, it uses the format `i-{input}` (plain text); otherwise, it applies Base64 encoding with the format `ie-{base64_encoded_input}`. +- You can force a specific method by setting encoding to `plain` (always use `i-{input}`) or `base64` (always use `ie-{base64}`). +- Note: In all cases, the text is percent-encoded to ensure URL safety. + +For image, video, and subtitle overlays: +- The input path is processed by removing any leading/trailing slashes and replacing inner slashes with `@@` when `plain` is used. +- Similarly, if `auto` is used, the SDK determines whether to apply plain text or Base64 encoding based on the characters present. +- For explicit behavior, use `plain` or `base64` to enforce the desired encoding. + +Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or `base64` when a specific encoding method is required. + +Below is a table describing these options: + +| Option | Description | Use Case | +| -------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | +| `auto` | SDK automatically selects between plain and base64 encoding based on the input. | Best for most cases when unsure or input is simple. | +| `plain` | SDK treats the input as plain text. | Use for inputs that are already URL-safe. | +| `base64` | SDK encodes the input using Base64 to ensure URL safety when special characters are present. | Use for complex inputs with characters that require encoding. | + +##### Solid Color Overlay Transformations + +| Option | Description | Example | +| ------ | ---------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| width | Specifies the width of the solid color overlay block (in pixels or as an arithmetic expression). | `width: 100` | +| height | Specifies the height of the solid color overlay block (in pixels or as an arithmetic expression). | `height: 50` | +| radius | Specifies the corner radius of the solid color overlay block or shape. Can be a number or `"max"` for circular/oval shapes. | `radius: "max"` | +| alpha | Specifies the transparency level of the solid color overlay. Supports integers from 1 (most transparent) to 9 (least transparent). | `alpha: 5` | + +##### Text Overlay Transformations + +| Option | Description | Example | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | +| `width` | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. | `width: 400` | +| `fontSize` | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | +| `fontFamily` | Specifies the font family of the overlaid text. Choose from the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) or provide a [custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). | `fontFamily: "Arial"` | +| `fontColor` | Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a standard color name. | `fontColor: "FF0000"` | +| `innerAlignment` | Specifies the inner alignment of the text when the content does not occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | +| `padding` | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | +| `alpha` | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | +| `typography` | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, `b_i` for both bold and italics. | `typography: "b"` | +| `background` | Specifies the background color of the text overlay. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. | `background: "red"` | +| `radius` | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` to achieve a circular/oval shape. | `radius: "max"` | +| `rotation` | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | +| `flip` | Specifies the flip or mirror option for the text overlay. Supported values: `h` (horizontal), `v` (vertical), `h_v` (both horizontal and vertical), `v_h` (alternative order). | `flip: "h"` | +| `lineHeight` | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | + +##### Subtitle Overlay Transformations + +| Option | Description | Example | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | +| `background` | Specifies the subtitle background color using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). | `background: "blue"` | +| `fontSize` | Sets the font size of subtitle text. Can be specified as a number. | `fontSize: 16` | +| `fontFamily` | Sets the font family of subtitle text. Refer to the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) for available options. | `fontFamily: "Arial"` | +| `color` | Specifies the font color of the subtitle text using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). | `color: "FF0000"` | +| `typography` | Sets the typography style of the subtitle text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | +| `fontOutline` | Specifies the font outline for subtitles. Requires the outline width (an integer) and the outline color (as a standard color name, RGB, or RGBA) separated by an underscore. Examples include `2_blue`, `2_A1CCDD`, or `2_A1CCDD50`. | `fontOutline: "2_blue"` | +| `fontShadow` | Specifies the font shadow for subtitles. Requires the shadow color (as a standard color name, RGB, or RGBA) and a shadow indent (an integer) separated by an underscore. Examples: `blue_2`, `A1CCDD_3`, or `A1CCDD50_3`. | `fontShadow: "blue_2"` | + +For image and video overlay transformation options, refer to the [ImageKit Transformations Documentation](https://imagekit.io/docs/transformations). #### AI and Advanced Transformations *Background Removal:* diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index ead7b23..7afba53 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -518,20 +518,19 @@ export interface TextOverlay extends BaseOverlay { type: "text"; /** - * Specifies the text to be displayed in the overlay. - * The SDK automatically handles special characters and URL encoding. + * Specifies the text to be displayed in the overlay. The SDK automatically handles special characters and encoding. */ text: string; /** - * Specifies how the overlay input text should be encoded. The default is `auto`, which means the SDK will initially treat the text as plain text to improve URL readability. If the text contains special characters, the SDK will automatically switch to `base64` encoding. - * - * You can also explicitly set the encoding to either `plain` or `base64`. - * - * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * Text can be included in the layer as either `i-{input}` (plain text) or `ie-{base64_encoded_input}` (base64). + * By default, the SDK selects the appropriate format based on the input text. + * To always use base64 (`ie-{base64}`), set this parameter to `base64`. + * To always use plain text (`i-{input}`), set it to `plain`. * * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. */ + encoding: "auto" | "plain" | "base64"; /** @@ -549,15 +548,14 @@ export interface ImageOverlay extends BaseOverlay { input: string; /** - * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. - * - * You can also explicitly set the encoding to either `plain` or `base64`. - * - * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + * By default, the SDK determines the appropriate format automatically. + * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + * To always use plain text (`i-{input}`), set it to `plain`. * * Regardless of the encoding method: * - Leading and trailing slashes are removed. - * - Any remaining slashes within the path are replaced with `@@` when using plain text. + * - Remaining slashes within the path are replaced with `@@` when using plain text. */ encoding: "auto" | "plain" | "base64"; @@ -577,15 +575,14 @@ export interface VideoOverlay extends BaseOverlay { input: string; /** - * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. - * - * You can also explicitly set the encoding to either `plain` or `base64`. - * - * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + * By default, the SDK determines the appropriate format automatically. + * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + * To always use plain text (`i-{input}`), set it to `plain`. * * Regardless of the encoding method: * - Leading and trailing slashes are removed. - * - Any remaining slashes within the path are replaced with `@@` when using plain text. + * - Remaining slashes within the path are replaced with `@@` when using plain text. */ encoding: "auto" | "plain" | "base64"; @@ -605,15 +602,14 @@ export interface SubtitleOverlay extends BaseOverlay { input: string; /** - * Specifies how the overlay input path should be encoded. The default is `auto`, which means the SDK will initially treat the path as plain text to improve URL readability. If the path contains special characters, the SDK will automatically switch to `base64` encoding. - * - * You can also explicitly set the encoding to either `plain` or `base64`. - * - * The `plain` option uses the format `i-{input}`, while `base64` uses `ie-{base64_encoded_input}`. + * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. + * By default, the SDK determines the appropriate format automatically. + * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. + * To always use plain text (`i-{input}`), set it to `plain`. * * Regardless of the encoding method: * - Leading and trailing slashes are removed. - * - Any remaining slashes within the path are replaced with `@@` when using plain text. + * - Remaining slashes within the path are replaced with `@@` when using plain text. */ encoding: "auto" | "plain" | "base64"; @@ -643,12 +639,12 @@ export interface SolidColorOverlay extends BaseOverlay { export type TextOverlayTransformation = { /** - * Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. Useful when used in conjunction with the `backgroundColor`. + * Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. Useful when used in conjunction with the `background`. */ width?: number | string; /** - * Specifies the font size of the overlaid text. Accepts a numeric value, a percentage, or an arithmetic expression. + * Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. */ fontSize?: number | string; From fbd63f02a205bb2d35975985aa1de4f655acdad2 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 14:50:12 +0530 Subject: [PATCH 089/166] docs: update README to clarify overlay options and improve encoding descriptions --- README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e05f0aa..ac6ec1f 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,11 @@ Download a specific version: ``` https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js ``` -Or for the latest version, remove the version number: +Or for the latest version, remove the version number (don't use in production as it may break your code if a new major version is released): ``` https://unpkg.com/imagekit-javascript/dist/imagekit.min.js ``` + And include it in your HTML: ```html @@ -250,6 +251,8 @@ var solidColorOverlayURL = imagekit.url({ ##### Overlay Options +ImageKit supports various overlay types, including text, image, video, subtitle, and solid color overlays. Each overlay type has specific configuration options to customize the overlay appearance and behavior. To learn more about how overlays work, refer to the [ImageKit documentation](https://imagekit.io/docs/transformations#overlay-using-layers). + The table below outlines the available overlay configuration options: | Option | Description | Example | @@ -260,8 +263,8 @@ The table below outlines the available overlay configuration options: | color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | | encoding | Defines how the overlay input is encoded. Accepted values: `auto`, `plain`, `base64`. | `encoding: "auto"` | | transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | -| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus` (e.g., `center`). | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | -| timing | (When base is a video) Defines when the overlay appears. Accepts an object with `start`, `duration`, and `end` properties (in seconds). | `timing: { start: 5, duration: 10 }` | +| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | +| timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | ##### Encoding Options @@ -280,14 +283,6 @@ For image, video, and subtitle overlays: Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or `base64` when a specific encoding method is required. -Below is a table describing these options: - -| Option | Description | Use Case | -| -------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -| `auto` | SDK automatically selects between plain and base64 encoding based on the input. | Best for most cases when unsure or input is simple. | -| `plain` | SDK treats the input as plain text. | Use for inputs that are already URL-safe. | -| `base64` | SDK encodes the input using Base64 to ensure URL safety when special characters are present. | Use for complex inputs with characters that require encoding. | - ##### Solid Color Overlay Transformations | Option | Description | Example | @@ -327,8 +322,6 @@ Below is a table describing these options: | `fontOutline` | Specifies the font outline for subtitles. Requires the outline width (an integer) and the outline color (as a standard color name, RGB, or RGBA) separated by an underscore. Examples include `2_blue`, `2_A1CCDD`, or `2_A1CCDD50`. | `fontOutline: "2_blue"` | | `fontShadow` | Specifies the font shadow for subtitles. Requires the shadow color (as a standard color name, RGB, or RGBA) and a shadow indent (an integer) separated by an underscore. Examples: `blue_2`, `A1CCDD_3`, or `A1CCDD50_3`. | `fontShadow: "blue_2"` | -For image and video overlay transformation options, refer to the [ImageKit Transformations Documentation](https://imagekit.io/docs/transformations). - #### AI and Advanced Transformations *Background Removal:* ```js From b4ce1dc7ec9730ab4940ed2cecd7072fd99b47bb Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 15:38:37 +0530 Subject: [PATCH 090/166] change default transformation position to query to better handle wildcard purge. Update test cases to reflect this change. Bump major version to reflect breaking change. --- package-lock.json | 2 +- package.json | 2 +- src/url/builder.ts | 21 +++++++++++++-------- src/utils/transformation.ts | 5 +++-- test/data/index.js | 1 + test/url-generation/basic.js | 23 +++++++++++++++++++---- test/url-generation/overlay.js | 14 ++++++++++++++ 7 files changed, 52 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1f0c3f..7f04be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "3.1.0", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1bbfb44..bdfe653 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "3.1.0", + "version": "4.0.0", "description": "Javascript SDK for using ImageKit.io in the browser", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", diff --git a/src/url/builder.ts b/src/url/builder.ts index d014230..932c969 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -45,10 +45,6 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { return ""; } - // if (opts.sdkVersion && opts.sdkVersion.trim() != "") { - // urlObj.searchParams.append("ik-sdk-version", opts.sdkVersion.trim()); - // } - for (var i in opts.queryParameters) { urlObj.searchParams.append(i, String(opts.queryParameters[i])); } @@ -56,14 +52,12 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { var transformationString = constructTransformationString(opts.transformation); if (transformationString && transformationString.length) { - if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { - urlObj.searchParams.append(TRANSFORMATION_PARAMETER, transformationString); - } else { + if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { urlObj.pathname = pathJoin([ TRANSFORMATION_PARAMETER + transformationUtils.getChainTransformDelimiter() + transformationString, urlObj.pathname, ]); - } + } } if (urlEndpointPattern) { @@ -72,6 +66,17 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { urlObj.pathname = pathJoin([urlObj.pathname]); } + if (transformationString && transformationString.length) { + if(transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { + if(urlObj.searchParams.toString() !== "") { // In 12 node.js .size was not there. So, we need to check if it is an object or not. + return `${urlObj.href}&${TRANSFORMATION_PARAMETER}=${transformationString}`; + } + else { + return `${urlObj.href}?${TRANSFORMATION_PARAMETER}=${transformationString}`; + } + } + } + return urlObj.href; }; diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index 0ef2c25..5034d0f 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -1,9 +1,10 @@ import supportedTransforms from "../constants/supportedTransforms"; import { ImageKitOptions, TransformationPosition } from "../interfaces"; -const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = "path"; const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; -const VALID_TRANSFORMATION_POSITIONS = [DEFAULT_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; +const PATH_TRANSFORMATION_POSITION: TransformationPosition = "path"; +const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = QUERY_TRANSFORMATION_POSITION; +const VALID_TRANSFORMATION_POSITIONS = [PATH_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; const CHAIN_TRANSFORM_DELIMITER: string = ":"; const TRANSFORM_DELIMITER: string = ","; const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; diff --git a/test/data/index.js b/test/data/index.js index d7676c2..1ec1645 100644 --- a/test/data/index.js +++ b/test/data/index.js @@ -1,4 +1,5 @@ module.exports.initializationParams = { publicKey: "test_public_key", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "path", } \ No newline at end of file diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index 739f240..b39f645 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -48,6 +48,21 @@ describe("URL generation", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); }); + it("By default transformationPosition should be query", function () { + var imagekitNew = new ImageKit({ + publicKey: "test_public_key", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + }); + const url = imagekitNew.url({ + path: "/test_path.jpg", + transformation: [{ + "height": "300", + "width": "400" + }] + }); + expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400"); + }); + it('should generate the URL without sdk version', function () { const ik = new ImageKit({ ...initializationParams, sdkVersion: "" }) @@ -111,7 +126,7 @@ describe("URL generation", function () { }] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300%2Cw-400`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); }); it('should generate the correct URL with a valid src parameter and transformation', function () { @@ -123,7 +138,7 @@ describe("URL generation", function () { }] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); }); it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { @@ -136,7 +151,7 @@ describe("URL generation", function () { }] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300%2Cw-400`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); }); it('should merge query parameters correctly in the generated URL', function () { @@ -149,7 +164,7 @@ describe("URL generation", function () { }] }); - 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=h-300,w-400`); }); diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 3a163f9..86eb13f 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -441,4 +441,18 @@ describe("Overlay encoding test cases", function () { }); expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitle,ie-${encodeURIComponent(safeBtoa("sub.srt"))},l-end/sample.mp4`); }); + + it("Avoid double encoding when transformation string is in query params", function () { + const url = imagekit.url({ + path: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", + text: "Minimal Text" + } + }], + transformationPosition: "query" + }); + expect(url).equal(`https://ik.imagekit.io/demo/sample.jpg?tr=l-text,i-Minimal%20Text,l-end`); + }); }); From 34a2b81ba8b65b948b4553acf625590c9a2ac3ad Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 16:29:06 +0530 Subject: [PATCH 091/166] refactor: remove sdkVersion from ImageKitOptions and update constructor accordingly --- README.md | 265 +++++++++++++++++------------- src/index.ts | 3 +- src/interfaces/ImageKitOptions.ts | 1 - test/url-generation/basic.js | 6 +- 4 files changed, 153 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index ac6ec1f..25b159d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Lightweight JavaScript SDK for generating optimized URLs for images and videos, - [Promise-based Upload Example](#promise-based-upload-example) - [Test Examples](#test-examples) - [Changelog](#changelog) +- [Options Reference](#options-reference) ## Installation @@ -66,29 +67,47 @@ And include it in your HTML: ## Initialization -Initialize the SDK by specifying your URL endpoint. You can obtain your URL endpoint from [https://imagekit.io/dashboard/url-endpoints](https://imagekit.io/dashboard/url-endpoints) and your public API key from [https://imagekit.io/dashboard/developer/api-keys](https://imagekit.io/dashboard/developer/api-keys). For URL generation: -```js -var imagekit = new ImageKit({ - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id" -}); -``` -For client-side file uploads, include your public key: +Initialize the SDK by specifying your URL endpoint. Obtain your URL endpoint from [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from [developer section](https://imagekit.io/dashboard/developer/api-keys). For URL generation: + ```js var imagekit = new ImageKit({ - publicKey: "your_public_api_key", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", // Required + transformationPosition: "query", // Optional. Default is "query" + publicKey: "your_public_api_key", // Optional. Only needef for client-side file uploads. }); ``` -*Note: Never include your private key in client-side code. If provided, the SDK throws an error.* + +> Note: Never include your private key in client-side code. If provided, the SDK throws an error. + +### Initialization Options + +| Option | Description | Example | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- | +| urlEndpoint | Required. It is always `https://ik.imagekit.io/your_imagekit_id` or configued custom domain name. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | +| transformationPosition | Optional. Determines the position of the transformation string in the URL. Accepts `path` (as URL segment) or `query` (as query parameter). Default value is `query` so that it is possible for you to issue a wild card purge to remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | +| publicKey | Optional. Your ImageKit public API key. Required for client-side file uploads. | `publicKey: "your_public_api_key"` | + ## URL Generation The SDK’s `.url()` method enables you to generate optimized image and video URLs with a variety of transformations. +The method accepts an object with the following parameters: + +| Option | Description | Example | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | +| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | +| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | +| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. | `[ { width: 300, height: 400 } ]` | +| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | + +Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings on per `.url()` call basis. + ### Basic URL Generation -1. **Using an Image Path with a URL Endpoint** - ```js +*A simple height and width transformation:* + +```js var imageURL = imagekit.url({ path: "/default-image.jpg", urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", @@ -97,26 +116,13 @@ The SDK’s `.url()` method enables you to generate optimized image and video UR width: 400 }] }); - ``` - *Result Example:* - ``` - https://ik.imagekit.io/your_imagekit_id/endpoint/tr:h-300,w-400/default-image.jpg - ``` - -2. **Using a Full Image URL (src)** - ```js - var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg", - transformation: [{ - height: 300, - width: 400 - }] - }); - ``` - *Result Example:* - ``` - https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cw-400 - ``` +``` +*Result Example:* +``` +https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,w-400 +``` + +SDK automatically generates the URL based on the provided parameters. The generated URL includes the base URL, path, and transformation parameters. ### Advanced URL Generation Examples @@ -131,12 +137,12 @@ var imageURL = imagekit.url({ }, { rotation: 90 }], - transformationPosition: "query" // Use query parameter for transformations }); ``` + *Result Example:* ``` -https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300%2Cw-400%3Art-90 +https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400:rt-90 ``` #### Overlays and Effects @@ -263,10 +269,9 @@ The table below outlines the available overlay configuration options: | color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | | encoding | Defines how the overlay input is encoded. Accepted values: `auto`, `plain`, `base64`. | `encoding: "auto"` | | transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | -| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | +| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | | timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | - ##### Encoding Options Overlay encoding options define how the overlay input is converted for URL construction. When set to `auto`, the SDK automatically determines whether to use plain text or Base64 encoding based on the input content. @@ -294,33 +299,33 @@ Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or ` ##### Text Overlay Transformations -| Option | Description | Example | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| `width` | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. | `width: 400` | -| `fontSize` | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | -| `fontFamily` | Specifies the font family of the overlaid text. Choose from the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) or provide a [custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). | `fontFamily: "Arial"` | -| `fontColor` | Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a standard color name. | `fontColor: "FF0000"` | -| `innerAlignment` | Specifies the inner alignment of the text when the content does not occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | -| `padding` | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | -| `alpha` | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | -| `typography` | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, `b_i` for both bold and italics. | `typography: "b"` | -| `background` | Specifies the background color of the text overlay. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. | `background: "red"` | -| `radius` | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` to achieve a circular/oval shape. | `radius: "max"` | -| `rotation` | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | -| `flip` | Specifies the flip or mirror option for the text overlay. Supported values: `h` (horizontal), `v` (vertical), `h_v` (both horizontal and vertical), `v_h` (alternative order). | `flip: "h"` | -| `lineHeight` | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | +| Option | Description | Example | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | +| width | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions are supported (e.g., `bw_mul_0.2` or `bh_div_2`). | `width: 400` | +| fontSize | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | +| fontFamily | Specifies the font family of the overlaid text. Choose from the supported fonts or provide a custom font. | `fontFamily: "Arial"` | +| fontColor | Specifies the font color of the overlaid text. Accepts an RGB hex code, an RGBA code, or a standard color name. | `fontColor: "FF0000"` | +| innerAlignment | Specifies the inner alignment of the text when it doesn’t occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | +| padding | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | +| alpha | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | +| typography | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | +| background | Specifies the background color of the text overlay. Accepts an RGB hex code, an RGBA code, or a color name. | `background: "red"` | +| radius | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` for circular/oval shape. | `radius: "max"` | +| rotation | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | +| flip | Specifies the flip option for the text overlay. Supported values: `h`, `v`, `h_v`, `v_h`. | `flip: "h"` | +| lineHeight | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | ##### Subtitle Overlay Transformations -| Option | Description | Example | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | -| `background` | Specifies the subtitle background color using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). | `background: "blue"` | -| `fontSize` | Sets the font size of subtitle text. Can be specified as a number. | `fontSize: 16` | -| `fontFamily` | Sets the font family of subtitle text. Refer to the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) for available options. | `fontFamily: "Arial"` | -| `color` | Specifies the font color of the subtitle text using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). | `color: "FF0000"` | -| `typography` | Sets the typography style of the subtitle text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | -| `fontOutline` | Specifies the font outline for subtitles. Requires the outline width (an integer) and the outline color (as a standard color name, RGB, or RGBA) separated by an underscore. Examples include `2_blue`, `2_A1CCDD`, or `2_A1CCDD50`. | `fontOutline: "2_blue"` | -| `fontShadow` | Specifies the font shadow for subtitles. Requires the shadow color (as a standard color name, RGB, or RGBA) and a shadow indent (an integer) separated by an underscore. Examples: `blue_2`, `A1CCDD_3`, or `A1CCDD50_3`. | `fontShadow: "blue_2"` | +| Option | Description | Example | +| ----------- | --------------------------------------------------------------------------------------------------------- | ----------------------- | +| background | Specifies the subtitle background color using a standard color name, RGB color code, or RGBA color code. | `background: "blue"` | +| fontSize | Sets the font size of subtitle text. | `fontSize: 16` | +| fontFamily | Sets the font family of subtitle text. | `fontFamily: "Arial"` | +| color | Specifies the font color of subtitle text using standard color name, RGB, or RGBA color code. | `color: "FF0000"` | +| typography | Sets the typography style of subtitle text. Supported values: `b`, `i`, `b_i`. | `typography: "b"` | +| fontOutline | Specifies the font outline for subtitles. Requires an outline width and color separated by an underscore. | `fontOutline: "2_blue"` | +| fontShadow | Specifies the font shadow for subtitles. Requires shadow color and indent separated by an underscore. | `fontShadow: "blue_2"` | #### AI and Advanced Transformations *Background Removal:* @@ -365,64 +370,65 @@ var imageURL = imagekit.url({ ### Supported 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. +The SDK gives a name to each transformation parameter (e.g. `height` maps to `h`, `width` maps to `w`). If the property does not match any of the following supported options, it is added as is. -If you want to generate transformations in your application and add them to the URL as it is, use the raw parameter. +If you want to generate transformations without any modifications, use the `raw` parameter. Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. -| Transformation Name | URL Parameter | -| -------------------------- | ------------------------------------------------------------- | -| width | w | -| height | h | -| aspectRatio | ar | -| quality | q | -| aiRemoveBackground | e-bgremove (ImageKit powered) | -| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | -| aiUpscale | e-upscale | -| aiRetouch | e-retouch | -| aiVariation | e-genvar | -| aiDropShadow | e-dropshadow | -| aiChangeBackground | e-changebg | -| crop | c | -| cropMode | cm | -| x | x | -| y | y | -| xCenter | xc | -| yCenter | yc | -| focus | fo | -| format | f | -| radius | r | -| background | bg | -| border | b | -| rotation | rt | -| blur | bl | -| named | n | -| dpr | dpr | -| progressive | pr | -| lossless | lo | -| trim | t | -| metadata | md | -| colorProfile | cp | -| defaultImage | di | -| original | orig | -| videoCodec | vc | -| audioCodec | ac | -| grayscale | e-grayscale | -| contrastStretch | e-contrast | -| shadow | e-shadow | -| sharpen | e-sharpen | -| unsharpMask | e-usm | -| gradient | e-gradient | -| flip | fl | -| opacity | o | -| zoom | z | -| page | pg | -| startOffset | so | -| endOffset | eo | -| duration | du | -| streamingResolutions | sr | -| raw | The string provided in raw will be added in the URL as it is. | +| Transformation Name | URL Parameter | +| -------------------------- | ---------------------------------------------------------------------------- | +| width | w | +| height | h | +| aspectRatio | ar | +| quality | q | +| aiRemoveBackground | e-bgremove (ImageKit powered) | +| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | +| aiUpscale | e-upscale | +| aiRetouch | e-retouch | +| aiVariation | e-genvar | +| aiDropShadow | e-dropshadow | +| aiChangeBackground | e-changebg | +| crop | c | +| cropMode | cm | +| x | x | +| y | y | +| xCenter | xc | +| yCenter | yc | +| focus | fo | +| format | f | +| radius | r | +| background | bg | +| border | b | +| rotation | rt | +| blur | bl | +| named | n | +| dpr | dpr | +| progressive | pr | +| lossless | lo | +| trim | t | +| metadata | md | +| colorProfile | cp | +| defaultImage | di | +| original | orig | +| videoCodec | vc | +| audioCodec | ac | +| grayscale | e-grayscale | +| contrastStretch | e-contrast | +| shadow | e-shadow | +| sharpen | e-sharpen | +| unsharpMask | e-usm | +| gradient | e-gradient | +| flip | fl | +| opacity | o | +| zoom | z | +| page | pg | +| startOffset | so | +| endOffset | eo | +| duration | du | +| streamingResolutions | sr | +| overlay | Generated correct layer syntax for image, video, text and subtitle overlays. | +| raw | The string provided in raw will be added in the URL as is. | ### Handling Unsupported Transformations @@ -436,7 +442,7 @@ var imageURL = imagekit.url({ newparam: "cool" }] }); -// Generated URL: https://ik.imagekit.io/test_url_endpoint/tr:newparam-cool/test_path.jpg +// Generated URL: https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=newparam-cool ``` ## File Upload @@ -448,6 +454,31 @@ The SDK offers a simple interface via the `.upload()` method to upload files to Before invoking the upload, generate the necessary security parameters as per the [ImageKit Upload API documentation](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). +### Upload Options +| Option | Description | Example | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | +| file | The file content to be uploaded. Accepts binary, base64 string, or URL. | `file: fileInput.files[0]` | +| fileName | The name to assign to the uploaded file. Supports alphanumeric characters, dot, underscore, and dash. | `fileName: "myImage.jpg"` | +| signature | HMAC-SHA1 digest computed using the private API key. Must be calculated on the server side. | `signature: "generated_signature"` | +| token | A unique token to prevent duplicate upload retries. Typically a V4 UUID or similar unique string. | `token: "unique_upload_token"` | +| expire | Unix timestamp (in seconds) indicating the signature expiry time (should be within 1 hour). | `expire: 1616161616` | +| useUniqueFileName | Boolean flag to automatically generate a unique filename if set to true. Defaults to true. | `useUniqueFileName: true` | +| folder | The folder path where the file will be uploaded. Automatically creates nested folders if they don’t exist. | `folder: "/images/uploads"` | +| isPrivateFile | Boolean to mark the file as private, restricting access to the original file URL. Defaults to false. | `isPrivateFile: false` | +| tags | Tags to associate with the file. Can be a comma-separated string or an array of tags. | `tags: "summer,holiday"` or `tags: ["summer","holiday"]` | +| customCoordinates | Specifies an area of interest in the image formatted as `x,y,width,height`. | `customCoordinates: "10,10,100,100"` | +| responseFields | Comma-separated list of fields to include in the upload response. | `responseFields: "tags,customCoordinates"` | +| extensions | Array of extension objects for additional image processing. | `extensions: [{ name: "auto-tagging" }]` | +| webhookUrl | URL to which the final status of extension processing will be sent. | `webhookUrl: "https://example.com/webhook"` | +| overwriteFile | Boolean flag indicating whether to overwrite a file if it exists. Defaults to true. | `overwriteFile: true` | +| overwriteAITags | Boolean flag to remove AITags from a file if overwritten. Defaults to true. | `overwriteAITags: true` | +| overwriteTags | Boolean flag that determines if existing tags should be removed when new tags are not provided. Defaults to true when file is overwritten without tags. | `overwriteTags: true` | +| overwriteCustomMetadata | Boolean flag dictating if existing custom metadata should be removed when not provided. Defaults to true under similar conditions as tags. | `overwriteCustomMetadata: true` | +| customMetadata | Stringified JSON or an object containing custom metadata key-value pairs to associate with the file. | `customMetadata: {author: "John Doe"}` | +| transformation | Optional transformation object to apply during the upload process. It follows the same structure as in URL generation. | `transformation: { pre: "w-200,h-200", post: [...] }` | +| xhr | An optional XMLHttpRequest object provided to monitor upload progress. | `xhr: new XMLHttpRequest()` | +| checks | Optional string value for specifying server-side checks to run before file upload. | `checks: "file.size' < '1MB'"` | + ### Basic Upload Example Below is an HTML form example that uses a callback for handling the upload response: @@ -503,9 +534,9 @@ imagekit.upload({ ## Test Examples For a quick demonstration of the SDK features, refer to our test examples: -- URL Generation examples can be found in [test/url-generation.js](./test/url-generation.js) +- URL Generation examples can be found in [basic](./test/url-generation/basic.js) and [overlay](./test/url-generation/overlay.js) - File Upload examples can be found in [test/upload.js](./test/upload.js) ## Changelog -For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). +For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 9b88b33..6594fa7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,13 +37,12 @@ const promisify = function (thisContext: ImageKit, fn: Function) { class ImageKit { options: ImageKitOptions = { - sdkVersion: `javascript-${version}`, publicKey: "", urlEndpoint: "", transformationPosition: transformationUtils.getDefault(), }; - constructor(opts: Omit) { + constructor(opts: ImageKitOptions) { this.options = { ...this.options, ...(opts || {}) }; if (!mandatoryParametersAvailable(this.options)) { throw errorMessages.MANDATORY_INITIALIZATION_MISSING; diff --git a/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts index d7394f0..6f8b78f 100644 --- a/src/interfaces/ImageKitOptions.ts +++ b/src/interfaces/ImageKitOptions.ts @@ -2,7 +2,6 @@ import { TransformationPosition } from "."; export interface ImageKitOptions { urlEndpoint: string; - sdkVersion?: string; publicKey?: string; transformationPosition?: TransformationPosition; } diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index b39f645..647b539 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -58,13 +58,15 @@ describe("URL generation", function () { transformation: [{ "height": "300", "width": "400" + }, { + rotation: 90 }] }); - expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400"); + expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90"); }); it('should generate the URL without sdk version', function () { - const ik = new ImageKit({ ...initializationParams, sdkVersion: "" }) + const ik = new ImageKit(initializationParams) const url = ik.url({ path: "/test_path.jpg", From 02033ee99137691c6e90173742c390f259e7443c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:05:49 +0530 Subject: [PATCH 092/166] refactor: remove deprecated transformation effects and update related tests --- src/constants/supportedTransforms.ts | 9 ------- src/interfaces/Transformation.ts | 36 ---------------------------- test/url-generation/basic.js | 18 +++++++------- 3 files changed, 9 insertions(+), 54 deletions(-) diff --git a/src/constants/supportedTransforms.ts b/src/constants/supportedTransforms.ts index 52a64c3..8353ceb 100644 --- a/src/constants/supportedTransforms.ts +++ b/src/constants/supportedTransforms.ts @@ -32,15 +32,6 @@ export const supportedTransforms: { [key: string]: string } = { duration: "du", streamingResolutions: "sr", - // Old deprecated mappings - effectSharpen: "e-sharpen", - effectUSM: "e-usm", - effectContrast: "e-contrast", - effectGray: "e-grayscale", - effectShadow: "e-shadow", - effectGradient: "e-gradient", - rotate: "rt", - // AI & advanced effects grayscale: "e-grayscale", aiUpscale: "e-upscale", diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 7afba53..adbe8dd 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -386,42 +386,6 @@ export interface Transformation { */ raw?: string; - // old as it is but deprecated - - /** - * @deprecated Use `rotation` instead. - */ - rotate?: string; - - /** - * @deprecated Use `sharpen` instead. - */ - effectSharpen?: string; - - /** - * @deprecated Use `unsharpMask` instead. - */ - effectUSM?: string; - - /** - * @deprecated Use `contrastStretch` instead. - */ - effectContrast?: string; - - /** - * @deprecated Use `grayscale` instead. - */ - effectGray?: string; - - /** - * @deprecated Use `shadow` instead. - */ - effectShadow?: string; - - /** - * @deprecated Use `gradient` instead. - */ - effectGradient?: string; /** * Specifies an overlay to be applied on the parent image or video. diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index 647b539..cb81ef2 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -278,7 +278,7 @@ describe("URL generation", function () { const url = imagekit.url({ path: "/test_path.jpg", transformation: [{ - effectContrast: "-" + contrastStretch: "-" }] }) @@ -291,7 +291,7 @@ describe("URL generation", function () { transformation: [{ defaultImage: "/test_path.jpg", quality: undefined, - effectContrast: null + contrastStretch: null }] }) @@ -303,7 +303,7 @@ describe("URL generation", function () { path: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", - effectContrast: false + contrastStretch: false }] }) @@ -910,12 +910,12 @@ describe("URL generation", function () { 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, - effectShadow: 'bl-15_st-40_x-10_y-N5', - effectGradient: 'from-red_to-white', + sharpen: 10, + unsharpMask: "2-2-0.8-0.024", + contrastStretch: true, + grayscale: true, + shadow: 'bl-15_st-40_x-10_y-N5', + gradient: 'from-red_to-white', original: true, raw: "h-200,w-300,l-image,i-logo.png,l-end" }] From fc2440b545e2df8ae732682d74e42b1287c194fe Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:09:38 +0530 Subject: [PATCH 093/166] docs: remove Options Reference section from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 25b159d..b994879 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ Lightweight JavaScript SDK for generating optimized URLs for images and videos, - [Promise-based Upload Example](#promise-based-upload-example) - [Test Examples](#test-examples) - [Changelog](#changelog) -- [Options Reference](#options-reference) ## Installation From 696105ad259384229b67d0f419efc789f5b6ff7b Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:20:08 +0530 Subject: [PATCH 094/166] chore: update CHANGELOG for version 4.0.0 with breaking changes and new features --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03d7092..1033ef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## Version 4.0.0 + +### Breaking Changes + +1. The default value for `transformationPosition` is now `query` instead of `path`. This change enables wildcard cache purging to remove CDN cache for all generated transformations. + + **Action Required:** + If you're using the `transformationPosition` parameter in the `url` method and want to apply transformations in the path, you must now explicitly set the value to `path`. The default is `query`. + +2. Removed the following deprecated parameters: + `effectSharpen`, `effectUSM`, `effectContrast`, `effectGray`, `effectShadow`, `effectGradient`, and `rotate`. + +### Other Changes + +1. Native support for overlays has been added. See the README for usage examples. +2. New AI-powered transformations are now supported: + `aiRemoveBackground`, `aiUpscale`, `aiVariation`, `aiDropShadow`, `aiChangeBackground`, and more. + *(Introduced in version 3.1.0)* + +--- + ## SDK Version 3.0.0 ### Breaking Changes From 64c537410cee02ba67053940c733f5a3d898c6ec Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:36:46 +0530 Subject: [PATCH 095/166] fix: update regex for overlay path and text to allow additional safe characters --- src/url/builder.ts | 4 ++-- test/url-generation/overlay.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/url/builder.ts b/src/url/builder.ts index 932c969..ceebc59 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -2,8 +2,8 @@ import { ImageKitOptions, UrlOptions } from "../interfaces"; import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; import transformationUtils, { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; -const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._,/ ]*$') -const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._, ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. +const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') +const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. function removeTrailingSlash(str: string) { if (typeof str == "string" && str[str.length - 1] == "/") { diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 86eb13f..028a2df 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -318,17 +318,17 @@ describe("Overlay encoding test cases", function () { expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-Manu,l-end/medium_cafe_B1iTdD0C.jpg`); }); - it('Simple text overlay with spaces and comma, should use i instead of ie', function () { + it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { const url = imagekit.url({ path: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "text", - text: "alnum123-._, ", + text: "alnum123-._ ", } }] }); - expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123-._, ")},l-end/medium_cafe_B1iTdD0C.jpg`); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123-._ ")},l-end/medium_cafe_B1iTdD0C.jpg`); }); it('Non simple text overlay, should use ie instead of i', function () { From c48d189b5a7f07fc14c4ab1f622b151621282e1a Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:42:32 +0530 Subject: [PATCH 096/166] fix: update SIMPLE_OVERLAY_TEXT_REGEX to allow only safe characters in overlay text --- src/url/builder.ts | 2 +- test/url-generation/overlay.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/url/builder.ts b/src/url/builder.ts index ceebc59..0c22dec 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -3,7 +3,7 @@ import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transfor import transformationUtils, { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') -const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. +const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. function removeTrailingSlash(str: string) { if (typeof str == "string" && str[str.length - 1] == "/") { diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 028a2df..0ca07aa 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -324,11 +324,11 @@ describe("Overlay encoding test cases", function () { transformation: [{ overlay: { type: "text", - text: "alnum123-._ ", + text: "alnum123._ ", } }] }); - expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123-._ ")},l-end/medium_cafe_B1iTdD0C.jpg`); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123._ ")},l-end/medium_cafe_B1iTdD0C.jpg`); }); it('Non simple text overlay, should use ie instead of i', function () { From ffe7b1adcc7f10a97530ba078cd113bb6bd53e03 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 17:44:33 +0530 Subject: [PATCH 097/166] Revert "fix: update SIMPLE_OVERLAY_TEXT_REGEX to allow only safe characters in overlay text" This reverts commit c48d189b5a7f07fc14c4ab1f622b151621282e1a. --- src/url/builder.ts | 2 +- test/url-generation/overlay.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/url/builder.ts b/src/url/builder.ts index 0c22dec..ceebc59 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -3,7 +3,7 @@ import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transfor import transformationUtils, { safeBtoa } from "../utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') -const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. +const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. function removeTrailingSlash(str: string) { if (typeof str == "string" && str[str.length - 1] == "/") { diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 0ca07aa..028a2df 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -324,11 +324,11 @@ describe("Overlay encoding test cases", function () { transformation: [{ overlay: { type: "text", - text: "alnum123._ ", + text: "alnum123-._ ", } }] }); - expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123._ ")},l-end/medium_cafe_B1iTdD0C.jpg`); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-text,i-${encodeURIComponent("alnum123-._ ")},l-end/medium_cafe_B1iTdD0C.jpg`); }); it('Non simple text overlay, should use ie instead of i', function () { From b47246e8a1edeb1f5dabf65d185ee5144415a199 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 18:03:15 +0530 Subject: [PATCH 098/166] update readme --- README.md | 148 +++++++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index b994879..388a52f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![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) -Lightweight JavaScript SDK for generating optimized URLs for images and videos, and for handling file uploads via ImageKit. +A lightweight JavaScript SDK for generating optimized URLs for images and videos, and for uploading files using ImageKit. ## Table of Contents - [Installation](#installation) @@ -65,26 +65,25 @@ And include it in your HTML: ``` ## Initialization - -Initialize the SDK by specifying your URL endpoint. Obtain your URL endpoint from [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from [developer section](https://imagekit.io/dashboard/developer/api-keys). For URL generation: +To use the SDK, initialize it with your ImageKit URL endpoint. You can get the URL endpoint [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from the [developer section](https://imagekit.io/dashboard/developer/api-keys): ```js var imagekit = new ImageKit({ urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", // Required - transformationPosition: "query", // Optional. Default is "query" - publicKey: "your_public_api_key", // Optional. Only needef for client-side file uploads. + transformationPosition: "query", // Optional, defaults to "query" + publicKey: "your_public_api_key", // Optional, required only for client-side file uploads }); ``` -> Note: Never include your private key in client-side code. If provided, the SDK throws an error. +> Note: Never include your private API key in client-side code. The SDK will throw an error if you do. ### Initialization Options -| Option | Description | Example | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- | -| urlEndpoint | Required. It is always `https://ik.imagekit.io/your_imagekit_id` or configued custom domain name. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | -| transformationPosition | Optional. Determines the position of the transformation string in the URL. Accepts `path` (as URL segment) or `query` (as query parameter). Default value is `query` so that it is possible for you to issue a wild card purge to remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | -| publicKey | Optional. Your ImageKit public API key. Required for client-side file uploads. | `publicKey: "your_public_api_key"` | +| Option | Description | Example | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | +| urlEndpoint | Required. Your ImageKit URL endpoint or custom domain. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | +| transformationPosition | Optional. Specifies whether transformations are added as URL path segments (`path`) or query parameters (`query`). The default is `query`, which allows you to perform wildcard purges and remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | +| publicKey | Optional. Your public API key for client-side uploads. | `publicKey: "your_public_api_key"` | ## URL Generation @@ -93,14 +92,14 @@ The SDK’s `.url()` method enables you to generate optimized image and video UR The method accepts an object with the following parameters: -| Option | Description | Example | -| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | -| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | -| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. | `[ { width: 300, height: 400 } ]` | -| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | +| Option | Description | Example | +| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | +| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | +| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | +| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. See [supported transformations](#supported-transformations). | `[ { width: 300, height: 400 } ]` | +| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | -Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings on per `.url()` call basis. +Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings for a specific `.url()` call. ### Basic URL Generation @@ -116,6 +115,7 @@ Optionally, you can include `transformationPosition` and `urlEndpoint` in the ob }] }); ``` + *Result Example:* ``` https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,w-400 @@ -375,59 +375,59 @@ If you want to generate transformations without any modifications, use the `raw` Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. -| Transformation Name | URL Parameter | -| -------------------------- | ---------------------------------------------------------------------------- | -| width | w | -| height | h | -| aspectRatio | ar | -| quality | q | -| aiRemoveBackground | e-bgremove (ImageKit powered) | -| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | -| aiUpscale | e-upscale | -| aiRetouch | e-retouch | -| aiVariation | e-genvar | -| aiDropShadow | e-dropshadow | -| aiChangeBackground | e-changebg | -| crop | c | -| cropMode | cm | -| x | x | -| y | y | -| xCenter | xc | -| yCenter | yc | -| focus | fo | -| format | f | -| radius | r | -| background | bg | -| border | b | -| rotation | rt | -| blur | bl | -| named | n | -| dpr | dpr | -| progressive | pr | -| lossless | lo | -| trim | t | -| metadata | md | -| colorProfile | cp | -| defaultImage | di | -| original | orig | -| videoCodec | vc | -| audioCodec | ac | -| grayscale | e-grayscale | -| contrastStretch | e-contrast | -| shadow | e-shadow | -| sharpen | e-sharpen | -| unsharpMask | e-usm | -| gradient | e-gradient | -| flip | fl | -| opacity | o | -| zoom | z | -| page | pg | -| startOffset | so | -| endOffset | eo | -| duration | du | -| streamingResolutions | sr | -| overlay | Generated correct layer syntax for image, video, text and subtitle overlays. | -| raw | The string provided in raw will be added in the URL as is. | +| Transformation Name | URL Parameter | +| -------------------------- | ---------------------------------------------------------------------------------------------- | +| width | w | +| height | h | +| aspectRatio | ar | +| quality | q | +| aiRemoveBackground | e-bgremove (ImageKit powered) | +| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | +| aiUpscale | e-upscale | +| aiRetouch | e-retouch | +| aiVariation | e-genvar | +| aiDropShadow | e-dropshadow | +| aiChangeBackground | e-changebg | +| crop | c | +| cropMode | cm | +| x | x | +| y | y | +| xCenter | xc | +| yCenter | yc | +| focus | fo | +| format | f | +| radius | r | +| background | bg | +| border | b | +| rotation | rt | +| blur | bl | +| named | n | +| dpr | dpr | +| progressive | pr | +| lossless | lo | +| trim | t | +| metadata | md | +| colorProfile | cp | +| defaultImage | di | +| original | orig | +| videoCodec | vc | +| audioCodec | ac | +| grayscale | e-grayscale | +| contrastStretch | e-contrast | +| shadow | e-shadow | +| sharpen | e-sharpen | +| unsharpMask | e-usm | +| gradient | e-gradient | +| flip | fl | +| opacity | o | +| zoom | z | +| page | pg | +| startOffset | so | +| endOffset | eo | +| duration | du | +| streamingResolutions | sr | +| overlay | Generates the correct layer syntax for image, video, text, subtitle, and solid color overlays. | +| raw | The string provided in raw will be added in the URL as is. | ### Handling Unsupported Transformations @@ -532,9 +532,9 @@ imagekit.upload({ ## Test Examples -For a quick demonstration of the SDK features, refer to our test examples: -- URL Generation examples can be found in [basic](./test/url-generation/basic.js) and [overlay](./test/url-generation/overlay.js) -- File Upload examples can be found in [test/upload.js](./test/upload.js) +For a quick demonstration of the SDK features, check the test suite: +- URL generation examples can be found in [basic](./test/url-generation/basic.js) and [overlay](./test/url-generation/overlay.js) +- File upload examples can be found in [test/upload.js](./test/upload.js) ## Changelog From 58c087f538f6b784c887a05c953e39f4ea620fbb Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 18:06:29 +0530 Subject: [PATCH 099/166] docs: improve README formatting and clarify encoding options --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 388a52f..2111387 100644 --- a/README.md +++ b/README.md @@ -106,14 +106,14 @@ Optionally, you can include `transformationPosition` and `urlEndpoint` in the ob *A simple height and width transformation:* ```js - var imageURL = imagekit.url({ - path: "/default-image.jpg", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation: [{ - height: 300, - width: 400 - }] - }); +var imageURL = imagekit.url({ + path: "/default-image.jpg", + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", + transformation: [{ + height: 300, + width: 400 + }] +}); ``` *Result Example:* @@ -266,7 +266,7 @@ The table below outlines the available overlay configuration options: | text | (For text overlays) The text content to display. | `text: "ImageKit"` | | input | (For image, video, or subtitle overlays) Relative path to the overlay asset. | `input: "logo.png"` or `input: "overlay-video.mp4"` | | color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | -| encoding | Defines how the overlay input is encoded. Accepted values: `auto`, `plain`, `base64`. | `encoding: "auto"` | +| encoding | Accepted values: `auto`, `plain`, `base64`. [Check this](#encoding-options) for more details. | `encoding: "auto"` | | transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | | position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | | timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | From 7e5a1d758e4d88100837f4ec05bce8b2f4c389ce Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 31 Mar 2025 22:13:56 +0530 Subject: [PATCH 100/166] fix: update package.json to remove duplicate entries and add new keywords --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index bdfe653..2e5d45f 100644 --- a/package.json +++ b/package.json @@ -56,13 +56,13 @@ "javascript", "sdk", "js", - "sdk", "image", "optimization", - "image", "transformation", - "image", - "resize" + "resize", + "upload", + "video", + "overlay" ], "author": "ImageKit Developer", "license": "MIT", From 909cb8d9a4a857ef7c8f964e131fbeb7e229d0a3 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 1 Apr 2025 09:20:33 +0530 Subject: [PATCH 101/166] docs: enhance README description for clarity and browser usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2111387..c79c405 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![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) -A lightweight JavaScript SDK for generating optimized URLs for images and videos, and for uploading files using ImageKit. +A lightweight JavaScript SDK for generating image and video URLs with transformations, and for uploading files directly from the browser to ImageKit. This SDK is intended for use in the browser only. For Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). ## Table of Contents - [Installation](#installation) From aabfd0075954dec2c857365940b4ef053c32a4db Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 1 Apr 2025 14:24:47 +0530 Subject: [PATCH 102/166] chore: update version to 4.0.1 and fix overlay position issue --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++-- package-lock.json | 2 +- package.json | 2 +- src/url/builder.ts | 4 ++-- test/url-generation/overlay.js | 2 +- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1033ef1..d98f048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Version 4.0.1 + +### Bug Fixes + +1. Fixed overlay position x,y issue. + +--- + ## Version 4.0.0 ### Breaking Changes diff --git a/README.md b/README.md index c79c405..d522861 100644 --- a/README.md +++ b/README.md @@ -533,8 +533,8 @@ imagekit.upload({ ## Test Examples For a quick demonstration of the SDK features, check the test suite: -- URL generation examples can be found in [basic](./test/url-generation/basic.js) and [overlay](./test/url-generation/overlay.js) -- File upload examples can be found in [test/upload.js](./test/upload.js) +- URL generation examples can be found in [basic.js](./test/url-generation/basic.js) and [overlay.js](./test/url-generation/overlay.js) files. +- File upload examples can be found in [test/upload.js](./test/upload.js). ## Changelog diff --git a/package-lock.json b/package-lock.json index 7f04be8..2377029 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2e5d45f..2d9a978 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "4.0.0", + "version": "4.0.1", "description": "Javascript SDK for using ImageKit.io in the browser", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", diff --git a/src/url/builder.ts b/src/url/builder.ts index ceebc59..cc49a1b 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -184,10 +184,10 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined const { x, y, focus } = position; if (x) { - entries.push(`lxo-${x}`); + entries.push(`lx-${x}`); } if (y) { - entries.push(`lyo-${y}`); + entries.push(`ly-${y}`); } if (focus) { entries.push(`lfo-${focus}`); diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 028a2df..52f67b4 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -268,7 +268,7 @@ describe("Overlay Transformation Test Cases", function () { ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lxo-10,lyo-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) }); }); From f2ce24b5ae304ac4c7a3da218ebb481127dd26e9 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 2 Apr 2025 11:55:29 +0530 Subject: [PATCH 103/166] feat: add upload progress callback and abort signal support --- src/constants/errorMessages.ts | 13 +++++++------ src/interfaces/UploadOptions.ts | 12 +++++++++++- src/upload/index.ts | 34 +++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 06d8b51..64d21f0 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -15,10 +15,11 @@ export default { help: "", }, INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, - MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: ""}, - MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: ""}, - MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: ""}, - 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: ""}, + MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }, + MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }, + MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }, + 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: "" }, + UPLOAD_ABORTED: { message: "Request aborted by the user", help: "" }, }; diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 9a45479..9e5bd47 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -145,5 +145,15 @@ export interface UploadOptions { /** * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. */ - checks?: string + checks?: string; + + /** + * Optional callback function that will be called with the progress event when the file is being uploaded. + */ + onProgress?: (event: ProgressEvent) => void; + + /** + * Optional AbortSignal object that can be used to abort the upload request + */ + signal?: AbortSignal; } diff --git a/src/upload/index.ts b/src/upload/index.ts index 3d4915a..47a1a13 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -1,7 +1,7 @@ import errorMessages from "../constants/errorMessages"; -import respond from "../utils/respond"; -import { request } from "../utils/request"; import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; +import { request } from "../utils/request"; +import respond from "../utils/respond"; export const upload = ( xhr: XMLHttpRequest, @@ -24,17 +24,17 @@ export const upload = ( return; } - if(!uploadOptions.token) { + if (!uploadOptions.token) { respond(true, errorMessages.MISSING_TOKEN, callback) return } - if(!uploadOptions.signature) { + if (!uploadOptions.signature) { respond(true, errorMessages.MISSING_SIGNATURE, callback) return } - if(!uploadOptions.expire) { + if (!uploadOptions.expire) { respond(true, errorMessages.MISSING_EXPIRE, callback) return } @@ -73,7 +73,7 @@ export const upload = ( if (key === "file" && typeof uploadOptions.file != "string") { formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { - formData.append('tags', uploadOptions.tags.join(",")); + formData.append('tags', uploadOptions.tags.join(",")); } else if (key === 'signature') { formData.append("signature", uploadOptions.signature); } else if (key === 'expire') { @@ -87,12 +87,12 @@ export const upload = ( } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); - } else if(key === "transformation" && typeof uploadOptions.transformation === "object" && + } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && uploadOptions.transformation !== null) { formData.append(key, JSON.stringify(uploadOptions.transformation)); } else if (key === 'checks' && uploadOptions.checks) { formData.append("checks", uploadOptions.checks); - } else if(uploadOptions[key] !== undefined) { + } else if (uploadOptions[key] !== undefined) { formData.append(key, String(uploadOptions[key])); } } @@ -100,5 +100,23 @@ export const upload = ( formData.append("publicKey", options.publicKey); + if (uploadOptions.onProgress) { + xhr.upload.onprogress = function (event: ProgressEvent) { + if (uploadOptions.onProgress) uploadOptions.onProgress(event) + }; + } + + if (uploadOptions.signal && uploadOptions.signal) { + if (uploadOptions.signal.aborted) { // If the signal is already aborted, return immediately with the reason + respond(true, uploadOptions.signal.reason ?? errorMessages.UPLOAD_ABORTED, callback); + return; + } + // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted + uploadOptions.signal.addEventListener("abort", function () { + xhr.abort(); + respond(true, this.reason, callback); + }); + } + request(xhr, formData, callback); }; From 10ae4cd7367a761b5d1b9db7ce9f4d706157e261 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 2 Apr 2025 12:37:06 +0530 Subject: [PATCH 104/166] feat: implement abort signal support and upload progress tracking in upload process --- samples/sample-app/views/index.pug | 9 +++++++++ src/upload/index.ts | 22 ++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug index 91d398f..4c53a49 100644 --- a/samples/sample-app/views/index.pug +++ b/samples/sample-app/views/index.pug @@ -23,6 +23,7 @@ html script(type='text/javascript' src="./imagekit.min.js") script. try { + window.controller = new AbortController(); var imagekit = new ImageKit({ publicKey: "!{publicKey}", urlEndpoint: "!{urlEndpoint}", @@ -51,9 +52,11 @@ html var statusEl = document.getElementById("status"); statusEl.innerHTML = "Uploading..."; + // Use this if you want to track upload progress var customXHR = new XMLHttpRequest(); customXHR.upload.addEventListener('progress', function (e) { + console.log("On progress event handler from customXHR"); if (e.loaded <= fileSize) { var percent = Math.round(e.loaded / fileSize * 100); console.log(`Uploaded ${percent}%`); @@ -94,6 +97,11 @@ html token: securityParametersObj.token, signature: securityParametersObj.signature, expire: securityParametersObj.expire, + signal: window.controller.signal, + onProgress: function(e) { + console.log("On progress event handler from SDK"); + console.log(e.loaded); + }, //- extensions: [ //- { //- name: "aws-auto-tagging", @@ -102,6 +110,7 @@ html //- } //- ], }, function(err, result) { + debugger; if (err) { statusEl.innerHTML = "Error uploading image. "+ err.message; console.log(err) diff --git a/src/upload/index.ts b/src/upload/index.ts index 47a1a13..c7da74d 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -93,6 +93,7 @@ export const upload = ( } else if (key === 'checks' && uploadOptions.checks) { formData.append("checks", uploadOptions.checks); } else if (uploadOptions[key] !== undefined) { + if (["onProgress", "signal"].includes(key)) continue; formData.append(key, String(uploadOptions[key])); } } @@ -106,15 +107,28 @@ export const upload = ( }; } - if (uploadOptions.signal && uploadOptions.signal) { + function onAbortHandler() { + xhr.abort(); + // Provide the reason or fallback error + // @ts-ignore for TypeScript versions lacking `signal.reason` + respond(true, uploadOptions.signal?.reason ?? errorMessages.UPLOAD_ABORTED, callback); + } + + if (uploadOptions.signal) { if (uploadOptions.signal.aborted) { // If the signal is already aborted, return immediately with the reason + // @ts-ignore for TypeScript versions lacking `signal.reason` respond(true, uploadOptions.signal.reason ?? errorMessages.UPLOAD_ABORTED, callback); return; } + // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted - uploadOptions.signal.addEventListener("abort", function () { - xhr.abort(); - respond(true, this.reason, callback); + uploadOptions.signal.addEventListener("abort", onAbortHandler); + + // On XHR completion (success, fail, or abort), remove just this abort handler + xhr.addEventListener("loadend", () => { + if (uploadOptions.signal) { + uploadOptions.signal.removeEventListener("abort", onAbortHandler); + } }); } From b4daa2913de7689931be9e8803bb53ecb8d2695b Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 2 Apr 2025 14:06:30 +0530 Subject: [PATCH 105/166] add test cases for onProgress and abort --- package-lock.json | 6 + package.json | 3 +- test/setup.js | 14 ++ test/upload.js | 470 ++++++++++++++++++++++++++-------------------- 4 files changed, 284 insertions(+), 209 deletions(-) create mode 100644 test/setup.js diff --git a/package-lock.json b/package-lock.json index 2377029..f9475b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1772,6 +1772,12 @@ "@types/node": "*" } }, + "abortcontroller-polyfill": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.8.tgz", + "integrity": "sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ==", + "dev": true + }, "agent-base": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", diff --git a/package.json b/package.json index 2d9a978..195bdb0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-typescript": "^8.2.1", "@types/node": "^15.6.1", + "abortcontroller-polyfill": "^1.7.8", "babel-plugin-transform-class-properties": "^6.24.1", "chai": "^4.2.0", "codecov": "^3.8.0", @@ -43,7 +44,7 @@ "dev": "rollup -c -w", "export-types": "tsc", "build": "rm -rf dist*;rollup -c && yarn export-types", - "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha \"test/**/*.js\"", + "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", "report-coverage": "codecov" }, diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 0000000..51de158 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,14 @@ +// test-setup.js (loaded before your tests) +global.FormData = require("formdata-node"); +global.Blob = require("web-file-polyfill").Blob +global.File = require("web-file-polyfill").File +const { AbortController, abortableFetch } = require('abortcontroller-polyfill/dist/cjs-ponyfill'); +global.AbortController = AbortController +global.ProgressEvent = class FakeProgressEvent { + constructor(type, init = {}) { + this.type = type; + this.lengthComputable = init.lengthComputable || false; + this.loaded = init.loaded || 0; + this.total = init.total || 0; + } +}; diff --git a/test/upload.js b/test/upload.js index 5f27bd4..427c009 100644 --- a/test/upload.js +++ b/test/upload.js @@ -1,8 +1,5 @@ const chai = require("chai"); const sinon = require("sinon"); -global.FormData = require("formdata-node"); -global.Blob = require("web-file-polyfill").Blob -global.File = require("web-file-polyfill").File const expect = chai.expect; const initializationParams = require("./data").initializationParams import ImageKit from "../src/index"; @@ -119,7 +116,7 @@ describe("File upload", function () { expect(callback.calledOnce).to.be.true; sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); }); - + it('Missing token', function () { const fileOptions = { fileName: "test_file_name", @@ -1093,241 +1090,241 @@ describe("File upload", function () { }); it("With pre and post transformation", async function () { - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); - await sleep(); - - var arg = server.requests[0].requestBody; - - expect(arg.get("file")).to.be.equal("test_file"); - expect(arg.get("fileName")).to.be.equal("test_file_name"); - expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); - expect(arg.get("useUniqueFileName")).to.be.equal("false"); - expect(arg.get("publicKey")).to.be.equal("test_public_key"); - expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); + await sleep(); + + var arg = server.requests[0].requestBody; + + expect(arg.get("file")).to.be.equal("test_file"); + expect(arg.get("fileName")).to.be.equal("test_file_name"); + expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); + expect(arg.get("useUniqueFileName")).to.be.equal("false"); + expect(arg.get("publicKey")).to.be.equal("test_public_key"); + expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); + + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); it("With pre transformation", async function () { - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { pre: "w-100" }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); - await sleep(); - - var arg = server.requests[0].requestBody; - - expect(arg.get("file")).to.be.equal("test_file"); - expect(arg.get("fileName")).to.be.equal("test_file_name"); - expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); - expect(arg.get("useUniqueFileName")).to.be.equal("false"); - expect(arg.get("publicKey")).to.be.equal("test_public_key"); - expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: { pre: "w-100" }, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); + await sleep(); + + var arg = server.requests[0].requestBody; + + expect(arg.get("file")).to.be.equal("test_file"); + expect(arg.get("fileName")).to.be.equal("test_file_name"); + expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); + expect(arg.get("useUniqueFileName")).to.be.equal("false"); + expect(arg.get("publicKey")).to.be.equal("test_public_key"); + expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); + + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); it("With post transformation", async function () { - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: [{ type: "transformation", value: "w-100" }] }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); - await sleep(); - - var arg = server.requests[0].requestBody; - - expect(arg.get("file")).to.be.equal("test_file"); - expect(arg.get("fileName")).to.be.equal("test_file_name"); - expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); - expect(arg.get("useUniqueFileName")).to.be.equal("false"); - expect(arg.get("publicKey")).to.be.equal("test_public_key"); - expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: { post: [{ type: "transformation", value: "w-100" }] }, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); + await sleep(); + + var arg = server.requests[0].requestBody; + + expect(arg.get("file")).to.be.equal("test_file"); + expect(arg.get("fileName")).to.be.equal("test_file_name"); + expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); + expect(arg.get("useUniqueFileName")).to.be.equal("false"); + expect(arg.get("publicKey")).to.be.equal("test_public_key"); + expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); + + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); it("Should return error for an invalid transformation", async function () { - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: {}, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "", - message: "Invalid transformation parameter. Please include at least pre, post, or both.", - }; - errorUploadResponse(500, errRes); - await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: {}, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "", + message: "Invalid transformation parameter. Please include at least pre, post, or both.", + }; + errorUploadResponse(500, errRes); + await sleep(); + 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 = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { pre: "" }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "", - message: "Invalid pre transformation parameter.", - }; - errorUploadResponse(500, errRes); - await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: { pre: "" }, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "", + message: "Invalid pre transformation parameter.", + }; + errorUploadResponse(500, errRes); + await sleep(); + 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 = { - ...securityParameters, - 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); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - errorUploadResponse(500, errRes); - await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + const fileOptions = { + ...securityParameters, + 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); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "", + message: "Invalid post transformation parameter.", + }; + errorUploadResponse(500, errRes); + await sleep(); + 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 = { - ...securityParameters, - 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); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - errorUploadResponse(500, errRes); - await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + const fileOptions = { + ...securityParameters, + 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); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "", + message: "Invalid post transformation parameter.", + }; + errorUploadResponse(500, errRes); + await sleep(); + 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 = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: { post: {} }, - }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "", - message: "Invalid post transformation parameter.", - }; - errorUploadResponse(500, errRes); - await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + transformation: { post: {} }, + }; + var callback = sinon.spy(); + + imagekit.upload(fileOptions, callback); + + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "", + message: "Invalid post transformation parameter.", + }; + errorUploadResponse(500, errRes); + await sleep(); + expect(callback.calledOnce).to.be.true; + sinon.assert.calledWith(callback, errRes, null); }); it("With checks option", async function () { const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file", - responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - checks: "'request.folder' : '/'", + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + responseFields: "tags, customCoordinates, isPrivateFile, metadata", + useUniqueFileName: false, + checks: "'request.folder' : '/'", }; var callback = sinon.spy(); - + imagekit.upload(fileOptions, callback); - + expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); await sleep(); - + var arg = server.requests[0].requestBody; expect(arg.get("file")).to.be.equal("test_file"); expect(arg.get("fileName")).to.be.equal("test_file_name"); @@ -1335,8 +1332,65 @@ describe("File upload", function () { expect(arg.get("useUniqueFileName")).to.be.equal("false"); expect(arg.get("publicKey")).to.be.equal("test_public_key"); expect(arg.get('checks')).to.be.equal("'request.folder' : '/'"); - + expect(callback.calledOnce).to.be.true; sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); }); + + it('onProgress callback is triggered during upload', async function () { + const progressSpy = sinon.spy(); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + onProgress: progressSpy + }; + var callback = sinon.spy(); + imagekit.upload(fileOptions, callback); + expect(server.requests.length).to.be.equal(1); + server.requests[0].uploadProgress({ lengthComputable: true, loaded: 50, total: 100 }); + + await sleep(); + expect(progressSpy.calledOnce).to.be.true; + successUploadResponse(); + await sleep(); + expect(progressSpy.calledTwice).to.be.true; // for 100% progress + sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + }); + + it('Abort signal aborts the upload', async function () { + const abortController = new AbortController(); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + signal: abortController.signal + }; + var callback = sinon.spy(); + imagekit.upload(fileOptions, callback); + expect(server.requests.length).to.be.equal(1); + abortController.abort(); + await sleep(); + // get arguments of the first call + expect(callback.calledOnce).to.be.true; + const args = callback.getCall(0).args; + expect(args[0].name).to.be.equal("AbortError"); + expect(args[1]).to.be.equal(null); + }); + + it('Abort signal aborts the upload with reason', async function () { + const abortController = new AbortController(); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + signal: abortController.signal + }; + var callback = sinon.spy(); + imagekit.upload(fileOptions, callback); + expect(server.requests.length).to.be.equal(1); + abortController.abort("abort reason"); + await sleep(); + sinon.assert.calledWith(callback, "abort reason", null); + }); }); From 09c47d2f93c75e4bc4070cbeece7ebf440cb20ea Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 3 Apr 2025 16:12:37 +0530 Subject: [PATCH 106/166] expose url, upload static functions. Other updates --- rollup.config.js | 3 +- src/index.ts | 108 +++++++------ src/interfaces/ImageKitOptions.ts | 13 +- src/interfaces/UrlOptions.ts | 51 ++---- src/upload/index.ts | 257 +++++++++++++++++------------- src/url/builder.ts | 16 +- src/url/index.ts | 12 -- src/utils/request.ts | 83 ---------- src/utils/respond.ts | 9 -- 9 files changed, 239 insertions(+), 313 deletions(-) delete mode 100644 src/url/index.ts delete mode 100644 src/utils/request.ts delete mode 100644 src/utils/respond.ts diff --git a/rollup.config.js b/rollup.config.js index 747e961..713494c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,8 +12,7 @@ export default [ output: { name: "ImageKit", file: pkg.browser, - format: "umd", - sourceMap: true, + format: "umd" }, plugins: [ nodeResolve({ extensions: [".ts"] }), diff --git a/src/index.ts b/src/index.ts index 6594fa7..ccb3aa6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,48 +1,24 @@ -import { version } from "../package.json"; import errorMessages from "./constants/errorMessages"; import { ImageKitOptions, UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; import IKResponse from "./interfaces/IKResponse"; -import { upload } from "./upload/index"; -import respond from "./utils/respond"; -import { url } from "./url/index"; +import { upload } from "./upload"; +import { buildURL } from "./url/builder"; import transformationUtils from "./utils/transformation"; +type MakeRequired = Omit & Required>; + function mandatoryParametersAvailable(options: ImageKitOptions) { return options.urlEndpoint; } -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 = { + options: MakeRequired = { publicKey: "", urlEndpoint: "", transformationPosition: transformationUtils.getDefault(), }; - constructor(opts: ImageKitOptions) { + constructor(opts: MakeRequired) { this.options = { ...this.options, ...(opts || {}) }; if (!mandatoryParametersAvailable(this.options)) { throw errorMessages.MANDATORY_INITIALIZATION_MISSING; @@ -54,38 +30,70 @@ class ImageKit { } /** - * A utility function to generate asset URL. It applies the specified transformations and other parameters to the URL. + * An instance method to generate URL for the given transformation parameters. This method is useful when you want to generate URL using the instance of the SDK without passing common parameters like `urlEndpoint` and `transformationPosition` every time. */ - url(urlOptions: UrlOptions): string { - return url(urlOptions, this.options); + url(urlOptions: UrlOptions & Partial>): string { + // Merge the options with the instance options + const options = { + ...this.options, + ...urlOptions, + }; + return ImageKit.url(options); } /** - * For uploading files directly from the browser to ImageKit.io. - * - * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload} + * A static method to generate URL for the given transformation parameters. This method is useful when you want to generate URL without creating an instance of the SDK. */ - upload(uploadOptions: UploadOptions, options?: Partial): Promise> - upload(uploadOptions: UploadOptions, callback: (err: Error | null, response: IKResponse | null) => void, options?: Partial): void; - upload(uploadOptions: UploadOptions, callbackOrOptions?: ((err: Error | null, response: IKResponse | null) => void) | Partial, options?: Partial): void | Promise> { - let callback; - if (typeof callbackOrOptions === 'function') { - callback = callbackOrOptions; - } else { - options = callbackOrOptions || {}; + static url(urlOptions: UrlOptions & Required> & Pick): string { + const options = urlOptions; + if (!options.urlEndpoint || options.urlEndpoint.length === 0) { + throw { + message: "urlEndpoint is required", + } } - if (!uploadOptions || typeof uploadOptions !== "object") { - return respond(true, errorMessages.INVALID_UPLOAD_OPTIONS, callback); + if (!options.src || options.src.length === 0) { + throw { + message: "src is required", + } } - var mergedOptions = { + return buildURL(options); + } + + /** + * An instance method to upload file to ImageKit.io. This method is useful when you want to upload file using the instance of the SDK without passing common parameters like `urlEndpoint` and `publicKey` every time. + */ + upload(uploadOptions: UploadOptions & Partial>): Promise> { + // Merge the options with the instance options + const options = { ...this.options, - ...options, + ...uploadOptions, }; + + return ImageKit.upload(options as UploadOptions & Required>); + } + + /** + * A static method to upload file to ImageKit.io. This method is useful when you want to upload file without creating an instance of the SDK. + */ + static upload(uploadOptions: UploadOptions & Required>): Promise> { + if (!uploadOptions.publicKey || uploadOptions.publicKey.length === 0) { + throw errorMessages.MISSING_PUBLIC_KEY; + } + const { xhr: userProvidedXHR } = uploadOptions || {}; delete uploadOptions.xhr; const xhr = userProvidedXHR || new XMLHttpRequest(); - return promisify>(this, upload)(xhr, uploadOptions, mergedOptions, callback); + + // Extract publicKey from uploadOptions + const { publicKey, ...rest } = uploadOptions; + return upload( + xhr, + rest, + { + publicKey, + } + ) } } -export default ImageKit; +export default ImageKit; \ No newline at end of file diff --git a/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts index 6f8b78f..6f8602d 100644 --- a/src/interfaces/ImageKitOptions.ts +++ b/src/interfaces/ImageKitOptions.ts @@ -1,7 +1,18 @@ import { TransformationPosition } from "."; export interface ImageKitOptions { - urlEndpoint: string; + /** + * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + */ + urlEndpoint?: string; + + /** + * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + */ publicKey?: string; + + /** + * By default, the transformation string is added as a query parameter in the URL e.g. `?tr=w-100,h-100`. If you want to add the transformation string in the path of the URL, set this to `path`. + */ transformationPosition?: TransformationPosition; } diff --git a/src/interfaces/UrlOptions.ts b/src/interfaces/UrlOptions.ts index 5d1d38c..728f683 100644 --- a/src/interfaces/UrlOptions.ts +++ b/src/interfaces/UrlOptions.ts @@ -1,55 +1,22 @@ -import { TransformationPosition } from "."; import { Transformation } from "./Transformation"; -export interface UrlOptionsBase { +export interface UrlOptions { /** - * 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. + * Accepts relative or absolute path of the resource. If relative path is provided, it is appended to the `urlEndpoint`. If absolute path is provided, `urlEndpoint` is ignored. + */ + src: string; + + /** + * An array of objects specifying the transformations to be applied in the URL. If more than one transformation is specified, they are applied in the order they are specified as chained transformations. * * {@link https://imagekit.io/docs/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 | number }; - /** - * 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; -} - -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 - * - * {@link https://github.com/imagekit-developer/imagekit-javascript#url-generation} - */ -export type UrlOptions = UrlOptionsSrc | UrlOptionsPath; +} \ No newline at end of file diff --git a/src/upload/index.ts b/src/upload/index.ts index c7da74d..ea8b55e 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -1,136 +1,179 @@ import errorMessages from "../constants/errorMessages"; import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; -import { request } from "../utils/request"; -import respond from "../utils/respond"; +import IKResponse from "../interfaces/IKResponse"; export const upload = ( xhr: XMLHttpRequest, uploadOptions: UploadOptions, - options: ImageKitOptions, - callback?: (err: Error | null, response: UploadResponse | null) => void, -) => { - 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 (!options.publicKey) { - respond(true, errorMessages.MISSING_PUBLIC_KEY, callback); - return; - } + options: Required> +): Promise> => { + return new Promise((resolve, reject) => { + if (!uploadOptions.file) { + return reject(errorMessages.MISSING_UPLOAD_FILE_PARAMETER) + } - if (!uploadOptions.token) { - respond(true, errorMessages.MISSING_TOKEN, callback) - return - } + if (!uploadOptions.fileName) { + return reject(errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER); + } - if (!uploadOptions.signature) { - respond(true, errorMessages.MISSING_SIGNATURE, callback) - return - } + if (!options.publicKey) { + return reject(errorMessages.MISSING_PUBLIC_KEY); + } - if (!uploadOptions.expire) { - respond(true, errorMessages.MISSING_EXPIRE, callback) - return - } + if (!uploadOptions.token) { + return reject(errorMessages.MISSING_TOKEN); + } - if (uploadOptions.transformation) { - if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { - respond(true, errorMessages.INVALID_TRANSFORMATION, callback); - return; + if (!uploadOptions.signature) { + return reject(errorMessages.MISSING_SIGNATURE); } - if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { - respond(true, errorMessages.INVALID_PRE_TRANSFORMATION, callback); - return; + + if (!uploadOptions.expire) { + return reject(errorMessages.MISSING_EXPIRE); } - 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; + + if (uploadOptions.transformation) { + if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { + return reject(errorMessages.INVALID_TRANSFORMATION); + } + if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { + return reject(errorMessages.INVALID_PRE_TRANSFORMATION); + 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)) { + return reject(errorMessages.INVALID_POST_TRANSFORMATION); + } else if (transformation.type === "transformation" && !transformation.value) { + return reject(errorMessages.INVALID_POST_TRANSFORMATION); + } } + } else { + return reject(errorMessages.INVALID_POST_TRANSFORMATION); } - } else { - respond(true, errorMessages.INVALID_POST_TRANSFORMATION, callback); - return; } } - } - var formData = new FormData(); - let key: keyof typeof uploadOptions; - for (key in uploadOptions) { - if (key) { - if (key === "file" && typeof uploadOptions.file != "string") { - formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); - } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { - formData.append('tags', uploadOptions.tags.join(",")); - } else if (key === 'signature') { - formData.append("signature", uploadOptions.signature); - } else if (key === 'expire') { - formData.append("expire", String(uploadOptions.expire)); - } else if (key === 'token') { - formData.append("token", uploadOptions.token); - } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { - formData.append('responseFields', uploadOptions.responseFields.join(",")); - } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { - formData.append('extensions', JSON.stringify(uploadOptions.extensions)); - } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && - !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { - formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); - } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && - uploadOptions.transformation !== null) { - formData.append(key, JSON.stringify(uploadOptions.transformation)); - } else if (key === 'checks' && uploadOptions.checks) { - formData.append("checks", uploadOptions.checks); - } else if (uploadOptions[key] !== undefined) { - if (["onProgress", "signal"].includes(key)) continue; - formData.append(key, String(uploadOptions[key])); + var formData = new FormData(); + let key: keyof typeof uploadOptions; + for (key in uploadOptions) { + if (key) { + if (key === "file" && typeof uploadOptions.file != "string") { + formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); + } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { + formData.append('tags', uploadOptions.tags.join(",")); + } else if (key === 'signature') { + formData.append("signature", uploadOptions.signature); + } else if (key === 'expire') { + formData.append("expire", String(uploadOptions.expire)); + } else if (key === 'token') { + formData.append("token", uploadOptions.token); + } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { + formData.append('responseFields', uploadOptions.responseFields.join(",")); + } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { + formData.append('extensions', JSON.stringify(uploadOptions.extensions)); + } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && + !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { + formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); + } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && + uploadOptions.transformation !== null) { + formData.append(key, JSON.stringify(uploadOptions.transformation)); + } else if (key === 'checks' && uploadOptions.checks) { + formData.append("checks", uploadOptions.checks); + } else if (uploadOptions[key] !== undefined) { + if (["onProgress", "signal"].includes(key)) continue; + formData.append(key, String(uploadOptions[key])); + } } } - } - formData.append("publicKey", options.publicKey); + formData.append("publicKey", options.publicKey); - if (uploadOptions.onProgress) { - xhr.upload.onprogress = function (event: ProgressEvent) { - if (uploadOptions.onProgress) uploadOptions.onProgress(event) - }; - } - - function onAbortHandler() { - xhr.abort(); - // Provide the reason or fallback error - // @ts-ignore for TypeScript versions lacking `signal.reason` - respond(true, uploadOptions.signal?.reason ?? errorMessages.UPLOAD_ABORTED, callback); - } + if (uploadOptions.onProgress) { + xhr.upload.onprogress = function (event: ProgressEvent) { + if (uploadOptions.onProgress) uploadOptions.onProgress(event) + }; + } - if (uploadOptions.signal) { - if (uploadOptions.signal.aborted) { // If the signal is already aborted, return immediately with the reason + function onAbortHandler() { + xhr.abort(); + // Provide the reason or fallback error // @ts-ignore for TypeScript versions lacking `signal.reason` - respond(true, uploadOptions.signal.reason ?? errorMessages.UPLOAD_ABORTED, callback); - return; + return reject(uploadOptions.signal?.reason ?? errorMessages.UPLOAD_ABORTED); } - // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted - uploadOptions.signal.addEventListener("abort", onAbortHandler); + if (uploadOptions.signal) { + if (uploadOptions.signal.aborted) { // If the signal is already aborted, return immediately with the reason + // @ts-ignore for TypeScript versions lacking `signal.reason` + return reject(uploadOptions.signal.reason ?? errorMessages.UPLOAD_ABORTED); + } - // On XHR completion (success, fail, or abort), remove just this abort handler - xhr.addEventListener("loadend", () => { - if (uploadOptions.signal) { - uploadOptions.signal.removeEventListener("abort", onAbortHandler); + // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted + uploadOptions.signal.addEventListener("abort", onAbortHandler); + + // On XHR completion (success, fail, or abort), remove just this abort handler + xhr.addEventListener("loadend", () => { + if (uploadOptions.signal) { + uploadOptions.signal.removeEventListener("abort", onAbortHandler); + } + }); + } + + xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); + xhr.onerror = function (e) { + return reject(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR); + } + xhr.onload = function () { + if (xhr.status === 200) { + try { + var body = JSON.parse(xhr.responseText); + var uploadResponse = addResponseHeadersAndBody(body, xhr); + return resolve(uploadResponse); + } catch (ex: any) { + return reject(ex); + } + } else { + try { + var body = JSON.parse(xhr.responseText); + var uploadError = addResponseHeadersAndBody(body, xhr); + return reject(uploadError) + } catch (ex: any) { + return reject(ex); + } } - }); - } + }; + xhr.send(formData); + }); - request(xhr, formData, callback); }; + + +const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse => { + let response = { ...body }; + const responseMetadata = { + statusCode: xhr.status, + headers: getResponseHeaderMap(xhr) + } + Object.defineProperty(response, "$ResponseMetadata", { + value: responseMetadata, + enumerable: false, + writable: false + }); + return response as IKResponse; +} + +function getResponseHeaderMap(xhr: XMLHttpRequest) { + const headers: Record = {}; + const responseHeaders = xhr.getAllResponseHeaders(); + if (Object.keys(responseHeaders).length) { + responseHeaders + .trim() + .split(/[\r\n]+/) + .map(value => value.split(/: /)) + .forEach(keyValue => { + headers[keyValue[0].trim()] = keyValue[1].trim(); + }); + } + return headers; +} \ No newline at end of file diff --git a/src/url/builder.ts b/src/url/builder.ts index cc49a1b..e5cea46 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -25,17 +25,19 @@ function pathJoin(parts: string[], sep?: string) { return parts.join(separator).replace(replace, separator); } -export const buildURL = (opts: UrlOptions & ImageKitOptions) => { - if (!opts.path && !opts.src) { +export const buildURL = (opts: UrlOptions & Required> & Pick) => { + if (!opts.src) { return ""; } + const isRelativePath = opts.src && opts.src.startsWith("/"); + var urlObj, isSrcParameterUsedForURL, urlEndpointPattern; try { - if (opts.path) { + if (isRelativePath) { urlEndpointPattern = new URL(opts.urlEndpoint).pathname; - urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.path])); + urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.src])); } else { urlObj = new URL(opts.src!); isSrcParameterUsedForURL = true; @@ -49,7 +51,7 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { urlObj.searchParams.append(i, String(opts.queryParameters[i])); } - var transformationString = constructTransformationString(opts.transformation); + var transformationString = generateTransformationString(opts.transformation); if (transformationString && transformationString.length) { if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { @@ -205,7 +207,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined entries.push(`ldu-${duration}`); } - const transformationString = constructTransformationString(transformation); + const transformationString = generateTransformationString(transformation); if (transformationString && transformationString.trim() !== "") entries.push(transformationString); @@ -214,7 +216,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined return entries.join(transformationUtils.getTransformDelimiter()); } -function constructTransformationString(transformation: Transformation[] | undefined) { +export const generateTransformationString = function(transformation: Transformation[] | undefined) { if (!Array.isArray(transformation)) { return ""; } diff --git a/src/url/index.ts b/src/url/index.ts deleted file mode 100644 index 8503b76..0000000 --- a/src/url/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - URL builder -*/ -import { ImageKitOptions, UrlOptions } from "../interfaces"; -import { buildURL } from "./builder"; - -export const url = (urlOpts: UrlOptions, defaultOptions: ImageKitOptions) => { - return buildURL({ - ...defaultOptions, - ...urlOpts, - }); -}; diff --git a/src/utils/request.ts b/src/utils/request.ts deleted file mode 100644 index fd7688d..0000000 --- a/src/utils/request.ts +++ /dev/null @@ -1,83 +0,0 @@ -import respond from "../utils/respond"; -import errorMessages from "../constants/errorMessages" -import { ImageKitOptions, UploadResponse } from "../interfaces"; -import IKResponse from "../interfaces/IKResponse"; - -interface SignatureResponse { - signature: string - expire: number - token: string -} - -function getResponseHeaderMap(xhr: XMLHttpRequest) { - const headers: Record = {}; - const responseHeaders = xhr.getAllResponseHeaders(); - if (Object.keys(responseHeaders).length) { - responseHeaders - .trim() - .split(/[\r\n]+/) - .map(value => value.split(/: /)) - .forEach(keyValue => { - headers[keyValue[0].trim()] = keyValue[1].trim(); - }); - } - return headers; -} - -const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse => { - let response = { ...body }; - const responseMetadata = { - statusCode: xhr.status, - headers: getResponseHeaderMap(xhr) - } - Object.defineProperty(response, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - return response as IKResponse; -} - -export const request = ( - uploadFileXHR: XMLHttpRequest, - formData: FormData, - callback?: (err: Error | null, response: UploadResponse | null) => void) => { - - uploadFile(uploadFileXHR, formData).then((result) => { - return respond(false, result, callback); - }, (ex) => { - return respond(true, ex, callback); - }); -} - -export const uploadFile = ( - uploadFileXHR: XMLHttpRequest, - formData: FormData -): Promise | Error> => { - return new Promise((resolve, reject) => { - uploadFileXHR.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); - uploadFileXHR.onerror = function (e) { - return reject(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR); - } - uploadFileXHR.onload = function () { - if (uploadFileXHR.status === 200) { - try { - var body = JSON.parse(uploadFileXHR.responseText); - var uploadResponse = addResponseHeadersAndBody(body, uploadFileXHR); - return resolve(uploadResponse); - } catch (ex: any) { - return reject(ex); - } - } else { - try { - var body = JSON.parse(uploadFileXHR.responseText); - var uploadError = addResponseHeadersAndBody(body, uploadFileXHR); - return reject(uploadError) - } catch (ex: any) { - return reject(ex); - } - } - }; - uploadFileXHR.send(formData); - }); -} diff --git a/src/utils/respond.ts b/src/utils/respond.ts deleted file mode 100644 index 06d02f6..0000000 --- a/src/utils/respond.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default function(isError: boolean, response: any, callback?: (err: Error | null, response: any) => void) { - if(typeof callback == "function") { - if(isError) { - callback(response, null); - } else { - callback(null, response); - } - } -}; \ No newline at end of file From 990cf2930f3d02c6b3e1e46450eb173f0a048930 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 3 Apr 2025 17:15:36 +0530 Subject: [PATCH 107/166] update test cases --- src/index.ts | 13 +- src/url/builder.ts | 7 +- test/upload.js | 527 ++++++++++++++------------------- test/url-generation/basic.js | 147 +++++---- test/url-generation/overlay.js | 48 +-- 5 files changed, 320 insertions(+), 422 deletions(-) diff --git a/src/index.ts b/src/index.ts index ccb3aa6..e34687e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,18 +45,7 @@ class ImageKit { * A static method to generate URL for the given transformation parameters. This method is useful when you want to generate URL without creating an instance of the SDK. */ static url(urlOptions: UrlOptions & Required> & Pick): string { - const options = urlOptions; - if (!options.urlEndpoint || options.urlEndpoint.length === 0) { - throw { - message: "urlEndpoint is required", - } - } - if (!options.src || options.src.length === 0) { - throw { - message: "src is required", - } - } - return buildURL(options); + return buildURL(urlOptions); } /** diff --git a/src/url/builder.ts b/src/url/builder.ts index e5cea46..fe8fe09 100644 --- a/src/url/builder.ts +++ b/src/url/builder.ts @@ -26,16 +26,19 @@ function pathJoin(parts: string[], sep?: string) { } export const buildURL = (opts: UrlOptions & Required> & Pick) => { + opts.urlEndpoint = opts.urlEndpoint || ""; + opts.src = opts.src || ""; + if (!opts.src) { return ""; } - const isRelativePath = opts.src && opts.src.startsWith("/"); + const isAbsoluteURL = opts.src.startsWith("http://") || opts.src.startsWith("https://"); var urlObj, isSrcParameterUsedForURL, urlEndpointPattern; try { - if (isRelativePath) { + if (!isAbsoluteURL) { urlEndpointPattern = new URL(opts.urlEndpoint).pathname; urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.src])); } else { diff --git a/test/upload.js b/test/upload.js index 427c009..0a9978a 100644 --- a/test/upload.js +++ b/test/upload.js @@ -1,10 +1,10 @@ const chai = require("chai"); const sinon = require("sinon"); const expect = chai.expect; -const initializationParams = require("./data").initializationParams +const initializationParams = require("./data").initializationParams; +import 'regenerator-runtime/runtime'; import ImageKit from "../src/index"; var requests, server; -import 'regenerator-runtime/runtime' const uploadSuccessResponseObj = { "fileId": "598821f949c0a938d57563bd", @@ -27,7 +27,7 @@ const securityParameters = { signature: "test_signature", expire: 123, token: "test_token" -} +}; function successUploadResponse() { server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -56,10 +56,8 @@ function errorUploadResponse(statusCode, obj) { } async function sleep(ms = 0) { - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve(); - }, ms); + return new Promise((resolve) => { + setTimeout(resolve, ms); }); } @@ -75,49 +73,52 @@ describe("File upload", function () { }); afterEach(() => { - // Like before we must clean up when tampering with globals. global.XMLHttpRequest.restore(); server.restore(); }); - it('Invalid options', function () { - var callback = sinon.spy(); - - imagekit.upload(undefined, callback); - expect(server.requests.length).to.be.equal(0); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Invalid uploadOptions parameter" }, null); + it('Invalid options', async function () { + try { + await imagekit.upload(undefined); + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ help: "", message: "Missing file parameter for upload" }); + } }); - it('Missing fileName', function () { + it('Missing fileName', async function () { const fileOptions = { ...securityParameters, file: "https://ik.imagekit.io/remote-url.jpg" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing fileName parameter for upload" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ help: "", message: "Missing fileName parameter for upload" }); + } }); - it('Missing file', function () { + it('Missing file', async function () { const fileOptions = { ...securityParameters, fileName: "test_file_name", }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ help: "", message: "Missing file parameter for upload" }); + } }); - it('Missing token', function () { + it('Missing token', async function () { const fileOptions = { fileName: "test_file_name", file: "test_file", @@ -125,15 +126,17 @@ describe("File upload", function () { expire: 123 }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + } }); - it('Missing signature', function () { + it('Missing signature', async function () { const fileOptions = { fileName: "test_file_name", file: "test_file", @@ -141,15 +144,17 @@ describe("File upload", function () { expire: 123 }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + } }); - it('Missing expire', function () { + it('Missing expire', async function () { const fileOptions = { fileName: "test_file_name", file: "test_file", @@ -157,28 +162,33 @@ describe("File upload", function () { signature: 'test_signature' }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + } }); - it('Missing public key', function () { + it('Missing public key', async function () { const fileOptions = { fileName: "test_file_name", file: "test_file" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback, { - publicKey: "" + const imagekitWithoutPublicKey = new ImageKit({ + urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", }); - expect(server.requests.length).to.be.equal(1); - sinon.assert.calledWith(callback, { message: "Missing public key for upload", help: "" }, null); + try { + const uploadPromise = imagekitWithoutPublicKey.upload(fileOptions); + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ message: "Missing public key for upload", help: "" }); + } }); it('Upload endpoint network error handling', async function () { @@ -188,17 +198,18 @@ describe("File upload", function () { file: "test_file" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); - // Simulate network error on upload API server.requests[0].error(); await sleep(); - sinon.assert.calledWith(callback, { message: "Request to ImageKit upload endpoint failed due to network error", help: "" }, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal({ message: "Request to ImageKit upload endpoint failed due to network error", help: "" }); + } }); it('Boolean handling', async function () { @@ -213,10 +224,7 @@ describe("File upload", function () { isPrivateFile: true }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -235,8 +243,8 @@ describe("File upload", function () { expect(arg.get('isPrivateFile')).to.be.equal('true'); expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Tag array handling', async function () { @@ -249,10 +257,7 @@ describe("File upload", function () { isPrivateFile: true }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -269,8 +274,8 @@ describe("File upload", function () { expect(arg.get('isPrivateFile')).to.be.equal('true'); expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Missing useUniqueFileName', async function () { @@ -282,10 +287,7 @@ describe("File upload", function () { isPrivateFile: true }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -304,8 +306,8 @@ describe("File upload", function () { expect(arg.get('customCoordinates')).to.be.equal(undefined); expect(arg.get('responseFields')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Missing isPrivateFile', async function () { @@ -316,10 +318,7 @@ describe("File upload", function () { tags: ["test_tag1", "test_tag2"] }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -338,8 +337,8 @@ describe("File upload", function () { expect(arg.get('customCoordinates')).to.be.equal(undefined); expect(arg.get('responseFields')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('With extensions parameter', async function () { @@ -361,10 +360,7 @@ describe("File upload", function () { ], webhookUrl: "https://your-domain/?appId=some-id" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -384,10 +380,10 @@ describe("File upload", function () { expect(arg.get('isPrivateFile')).to.be.equal('true'); expect(arg.get('publicKey')).to.be.equal('test_public_key'); expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id') + expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id'); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Bare minimum request', async function () { @@ -398,10 +394,7 @@ describe("File upload", function () { tags: undefined }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -420,22 +413,19 @@ describe("File upload", function () { expect(arg.get('customCoordinates')).to.be.equal(undefined); expect(arg.get('responseFields')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Bare minimum request: Blob', async function () { - const buffer = Buffer.from("test_buffer") + const buffer = Buffer.from("test_buffer"); const fileOptions = { ...securityParameters, fileName: "test_file_name", file: buffer }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -455,8 +445,8 @@ describe("File upload", function () { expect(arg.get('customCoordinates')).to.be.equal(undefined); expect(arg.get('responseFields')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Error during upload', async function () { @@ -466,20 +456,21 @@ describe("File upload", function () { file: "test_file" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { help: "For support kindly contact us at support@imagekit.io .", message: "Your account cannot be authenticated." - } + }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it('Error during upload non 2xx with bad body', async function () { @@ -489,10 +480,7 @@ describe("File upload", function () { file: "test_file" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -504,9 +492,12 @@ describe("File upload", function () { ); server.respond(); await sleep(); - expect(callback.calledOnce).to.be.true; - var error = callback.args[0][0]; - expect(error instanceof SyntaxError).to.be.true; + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex instanceof SyntaxError).to.be.true; + } }); it('Error during upload 2xx with bad body', async function () { @@ -516,10 +507,7 @@ describe("File upload", function () { file: "test_file" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -531,9 +519,12 @@ describe("File upload", function () { ); server.respond(); await sleep(); - expect(callback.calledOnce).to.be.true; - var error = callback.args[0][0]; - expect(error instanceof SyntaxError).to.be.true; + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex instanceof SyntaxError).to.be.true; + } }); it('Upload via URL', async function () { @@ -543,10 +534,7 @@ describe("File upload", function () { file: "https://ik.imagekit.io/remote-url.jpg" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -565,8 +553,8 @@ describe("File upload", function () { expect(arg.get('customCoordinates')).to.be.equal(undefined); expect(arg.get('responseFields')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Overriding public key', async function () { @@ -578,9 +566,8 @@ describe("File upload", function () { file: "https://ik.imagekit.io/remote-url.jpg" }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback, { + const uploadPromise = imagekit.upload({ + ...fileOptions, publicKey: newPublicKey }); @@ -604,8 +591,8 @@ describe("File upload", function () { expect(arg.get('extensions')).to.be.equal(undefined); expect(arg.get('customMetadata')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('With overwrite parameters', async function () { @@ -619,21 +606,14 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ], overwriteFile: false, overwriteAITags: false, overwriteTags: false, overwriteCustomMetadata: false }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -658,8 +638,8 @@ describe("File upload", function () { expect(arg.get('overwriteTags')).to.be.equal('false'); expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('With customMetadata', async function () { @@ -673,11 +653,7 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ], overwriteFile: false, overwriteAITags: false, @@ -688,10 +664,7 @@ describe("File upload", function () { color: "red" }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -717,8 +690,8 @@ describe("File upload", function () { expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Array type fields', async function () { @@ -732,11 +705,7 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ], overwriteFile: false, overwriteAITags: false, @@ -747,10 +716,7 @@ describe("File upload", function () { color: "red" }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -776,8 +742,8 @@ describe("File upload", function () { expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('check custom XHR object is used', async function () { @@ -794,16 +760,11 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ], xhr }; - var callback = sinon.spy(); - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); expect(server.requests[0]).to.be.equal(xhr); expect(server.requests[0].onprogress.toString()).to.be.equal(fun.toString()); @@ -826,8 +787,8 @@ describe("File upload", function () { expect(arg.get('publicKey')).to.be.equal('test_public_key'); expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Upload using promise - success', async function () { @@ -841,15 +802,11 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ] }; - var uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); @@ -870,7 +827,7 @@ describe("File upload", function () { expect(arg.get('isPrivateFile')).to.be.equal('true'); expect(arg.get('publicKey')).to.be.equal('test_public_key'); expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - var response = await uploadPromise; + const response = await uploadPromise; expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); @@ -878,7 +835,7 @@ describe("File upload", function () { var errRes = { help: "For support kindly contact us at support@imagekit.io .", message: "Your account cannot be authenticated." - } + }; const fileOptions = { ...securityParameters, fileName: "test_file_name", @@ -889,20 +846,17 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ] }; try { - var uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = imagekit.upload(fileOptions); await sleep(); errorUploadResponse(500, errRes); await sleep(); - var response = await uploadPromise; + await uploadPromise; + throw new Error('Should have thrown error'); } catch (ex) { expect(ex).to.be.deep.equal(errRes); } @@ -922,19 +876,14 @@ describe("File upload", function () { useUniqueFileName: false, isPrivateFile: true, extensions: [ - { - name: "aws-auto-tagging", - minConfidence: 80, - maxTags: 10 - } + { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } ], xhr }; - var uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); - await sleep(); successUploadResponse(); await sleep(); @@ -957,7 +906,7 @@ describe("File upload", function () { expect(arg.get('publicKey')).to.be.equal('test_public_key'); expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - var response = await uploadPromise; + const response = await uploadPromise; expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); @@ -999,48 +948,11 @@ describe("File upload", function () { server.respond(); await sleep(); - var response = await uploadPromise; + const response = await uploadPromise; expect(response.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); expect(response.$ResponseMetadata.statusCode).to.be.deep.equal(200); }); - it('$ResponseMetadata assertions using callback', async function () { - var dummyResonseHeaders = { - "Content-Type": "application/json", - "x-request-id": "sdfsdfsdfdsf" - }; - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: "test_file" - }; - var callback = sinon.spy(); - imagekit.upload(fileOptions, callback); - - expect(server.requests.length).to.be.equal(1); - - await sleep(); - server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", - [ - 200, - dummyResonseHeaders, - JSON.stringify(uploadSuccessResponseObj) - ] - ); - server.respond(); - await sleep(); - - expect(callback.calledOnce).to.be.true; - - var callBackArguments = callback.args[0]; - expect(callBackArguments.length).to.be.eq(2); - var callbackResult = callBackArguments[1]; - - expect(callbackResult).to.be.deep.equal(uploadSuccessResponseObj); - expect(callbackResult.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); - expect(callbackResult.$ResponseMetadata.statusCode).to.be.deep.equal(200); - }); - it('Undefined fields should not be sent', async function () { const fileOptions = { ...securityParameters, @@ -1060,10 +972,7 @@ describe("File upload", function () { customMetadata: undefined }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1085,8 +994,9 @@ describe("File upload", function () { expect(arg.get('overwriteTags')).to.be.equal(undefined); expect(arg.get('overwriteCustomMetadata')).to.be.equal(undefined); expect(arg.get('customMetadata')).to.be.equal(undefined); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it("With pre and post transformation", async function () { @@ -1098,10 +1008,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1116,8 +1023,8 @@ describe("File upload", function () { expect(arg.get("publicKey")).to.be.equal("test_public_key"); expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it("With pre transformation", async function () { @@ -1129,10 +1036,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "w-100" }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1147,8 +1051,8 @@ describe("File upload", function () { expect(arg.get("publicKey")).to.be.equal("test_public_key"); expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it("With post transformation", async function () { @@ -1160,10 +1064,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "transformation", value: "w-100" }] }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1178,8 +1079,8 @@ describe("File upload", function () { expect(arg.get("publicKey")).to.be.equal("test_public_key"); expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it("Should return error for an invalid transformation", async function () { @@ -1191,10 +1092,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: {}, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1203,8 +1101,12 @@ describe("File upload", function () { }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it("Should return error for an invalid pre transformation", async function () { @@ -1216,10 +1118,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "" }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1228,8 +1127,12 @@ describe("File upload", function () { }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it("Should return error for an invalid post transformation of type abs", async function () { @@ -1241,10 +1144,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "abs", value: "" }] }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1253,8 +1153,12 @@ describe("File upload", function () { }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it("Should return error for an invalid post transformation of type transformation", async function () { @@ -1266,10 +1170,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "transformation", value: "" }] }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1278,8 +1179,12 @@ describe("File upload", function () { }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it("Should return error for an invalid post transformation if it's not an array", async function () { @@ -1291,10 +1196,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: {} }, }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1303,8 +1205,12 @@ describe("File upload", function () { }; errorUploadResponse(500, errRes); await sleep(); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, errRes, null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal(errRes); + } }); it("With checks option", async function () { @@ -1316,10 +1222,7 @@ describe("File upload", function () { useUniqueFileName: false, checks: "'request.folder' : '/'", }; - var callback = sinon.spy(); - - imagekit.upload(fileOptions, callback); - + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1333,8 +1236,8 @@ describe("File upload", function () { expect(arg.get("publicKey")).to.be.equal("test_public_key"); expect(arg.get('checks')).to.be.equal("'request.folder' : '/'"); - expect(callback.calledOnce).to.be.true; - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('onProgress callback is triggered during upload', async function () { @@ -1345,8 +1248,7 @@ describe("File upload", function () { file: "test_file", onProgress: progressSpy }; - var callback = sinon.spy(); - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); server.requests[0].uploadProgress({ lengthComputable: true, loaded: 50, total: 100 }); @@ -1355,7 +1257,8 @@ describe("File upload", function () { successUploadResponse(); await sleep(); expect(progressSpy.calledTwice).to.be.true; // for 100% progress - sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); + const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); it('Abort signal aborts the upload', async function () { @@ -1366,16 +1269,16 @@ describe("File upload", function () { file: "test_file", signal: abortController.signal }; - var callback = sinon.spy(); - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); abortController.abort(); await sleep(); - // get arguments of the first call - expect(callback.calledOnce).to.be.true; - const args = callback.getCall(0).args; - expect(args[0].name).to.be.equal("AbortError"); - expect(args[1]).to.be.equal(null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex.name).to.be.equal("AbortError"); + } }); it('Abort signal aborts the upload with reason', async function () { @@ -1386,11 +1289,15 @@ describe("File upload", function () { file: "test_file", signal: abortController.signal }; - var callback = sinon.spy(); - imagekit.upload(fileOptions, callback); + const uploadPromise = imagekit.upload(fileOptions); expect(server.requests.length).to.be.equal(1); abortController.abort("abort reason"); await sleep(); - sinon.assert.calledWith(callback, "abort reason", null); + try { + await uploadPromise; + throw new Error('Should have thrown error'); + } catch (ex) { + expect(ex).to.be.deep.equal("abort reason"); + } }); }); diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index cb81ef2..cdc57d2 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -18,12 +18,12 @@ describe("URL generation", function () { it('should return an empty string for an invalid src URL', function () { const url = imagekit.url({ src: "/" }); - expect(url).equal(""); + expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); }); it('should generate a valid URL when a path is provided without transformation', function () { const url = imagekit.url({ - path: "/test_path.jpg" + src: "/test_path.jpg" }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); @@ -39,10 +39,9 @@ describe("URL generation", function () { it('should generate a valid URL when undefined transformation parameters are provided with path', function () { const url = imagekit.url({ - path: "/test_path_alt.jpg", + src: "/test_path_alt.jpg", transformation: undefined, transformationPosition: undefined, - src: undefined, }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); @@ -54,7 +53,7 @@ describe("URL generation", function () { urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", }); const url = imagekitNew.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -69,7 +68,7 @@ describe("URL generation", function () { const ik = new ImageKit(initializationParams) const url = ik.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -81,7 +80,7 @@ describe("URL generation", function () { it('should generate the correct URL with a valid path and transformation', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -93,7 +92,7 @@ describe("URL generation", function () { it('should generate the correct URL when the provided path contains multiple leading slashes', function () { const url = imagekit.url({ - path: "///test_path.jpg", + src: "///test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -107,7 +106,7 @@ describe("URL generation", function () { it('should generate the correct URL when the urlEndpoint is overridden', function () { const url = imagekit.url({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint_alt", - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -120,7 +119,7 @@ describe("URL generation", function () { it('should generate the correct URL with transformationPosition as query parameter when path is provided', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformationPosition: "query", transformation: [{ "height": "300", @@ -172,7 +171,7 @@ describe("URL generation", function () { it('should generate the correct URL with chained transformations', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -187,7 +186,7 @@ describe("URL generation", function () { it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400" @@ -201,7 +200,7 @@ describe("URL generation", function () { it('should generate the correct URL when overlay image transformation is provided', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400", @@ -214,7 +213,7 @@ describe("URL generation", function () { it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400", @@ -227,7 +226,7 @@ describe("URL generation", function () { it('should generate the correct URL when border transformation is applied', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "height": "300", "width": "400", @@ -240,7 +239,7 @@ describe("URL generation", function () { it('should generate the correct URL when transformation has empty key and value', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "": "" }] @@ -254,7 +253,7 @@ describe("URL generation", function () { */ it('should generate the correct URL when an undefined transform is provided', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ "undefined-transform": "true" }] @@ -265,7 +264,7 @@ describe("URL generation", function () { it('should generate the correct URL when transformation key has an empty value', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ defaultImage: "" }] @@ -276,7 +275,7 @@ describe("URL generation", function () { it('should generate the correct URL when transformation key has \'-\' as its value', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ contrastStretch: "-" }] @@ -287,7 +286,7 @@ describe("URL generation", function () { it('should skip transformation parameters that are undefined or null', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", quality: undefined, @@ -300,7 +299,7 @@ describe("URL generation", function () { it('should skip transformation parameters that are false', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", contrastStretch: false @@ -312,7 +311,7 @@ describe("URL generation", function () { it('should include only the key when transformation value is an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", shadow: "" @@ -324,7 +323,7 @@ describe("URL generation", function () { it('should include both key and value when transformation parameter value is provided', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", shadow: "bl-15_st-40_x-10_y-N5" @@ -336,7 +335,7 @@ describe("URL generation", function () { it('should generate the correct URL when trim transformation is set to true as a boolean', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", trim: true @@ -348,7 +347,7 @@ describe("URL generation", function () { it('should generate the correct URL when trim transformation is set to true as a string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ defaultImage: "/test_path.jpg", trim: "true" @@ -360,7 +359,7 @@ describe("URL generation", function () { it('should generate the correct URL for AI background removal when set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackground: true }] @@ -371,7 +370,7 @@ describe("URL generation", function () { it('should generate the correct URL for AI background removal when \'true\' is provided as a string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackground: "true" }] @@ -382,7 +381,7 @@ describe("URL generation", function () { it('should not apply AI background removal when value is not true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackground: "false" }] @@ -393,7 +392,7 @@ describe("URL generation", function () { it('should generate the correct URL for external AI background removal when set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackgroundExternal: true }] @@ -404,7 +403,7 @@ describe("URL generation", function () { it('should generate the correct URL for external AI background removal when \'true\' is provided as a string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackgroundExternal: "true" }] @@ -415,7 +414,7 @@ describe("URL generation", function () { it('should not apply external AI background removal when value is not true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiRemoveBackgroundExternal: "false" }] @@ -426,7 +425,7 @@ describe("URL generation", function () { it('should generate the correct URL when gradient transformation is provided as a string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ gradient: "ld-top_from-green_to-00FF0010_sp-1" }] @@ -437,7 +436,7 @@ describe("URL generation", function () { it('should generate the correct URL when gradient transformation is provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ gradient: "" }] @@ -448,7 +447,7 @@ describe("URL generation", function () { it('should generate the correct URL when gradient transformation is set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ gradient: true }] @@ -459,7 +458,7 @@ describe("URL generation", function () { it('should generate the correct URL when AI drop shadow transformation is set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiDropShadow: true }] @@ -470,7 +469,7 @@ describe("URL generation", function () { it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiDropShadow: "" }] @@ -481,7 +480,7 @@ describe("URL generation", function () { it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aiDropShadow: "az-45" }] @@ -492,7 +491,7 @@ describe("URL generation", function () { it('should generate the correct URL when shadow transformation is set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ shadow: true }] @@ -503,7 +502,7 @@ describe("URL generation", function () { it('should generate the correct URL when shadow transformation is provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ shadow: "" }] @@ -514,7 +513,7 @@ describe("URL generation", function () { it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ shadow: "bl-15_st-40_x-10_y-N5" }] @@ -525,7 +524,7 @@ describe("URL generation", function () { it('should generate the correct URL when sharpen transformation is set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ sharpen: true }] @@ -536,7 +535,7 @@ describe("URL generation", function () { it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ sharpen: "" }] @@ -547,7 +546,7 @@ describe("URL generation", function () { it('should generate the correct URL when sharpen transformation is provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ sharpen: 10 }] @@ -558,7 +557,7 @@ describe("URL generation", function () { it('should generate the correct URL when unsharpMask transformation is set to true', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ unsharpMask: true }] @@ -569,7 +568,7 @@ describe("URL generation", function () { it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ unsharpMask: "" }] @@ -580,7 +579,7 @@ describe("URL generation", function () { it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ unsharpMask: "2-2-0.8-0.024" }] @@ -591,7 +590,7 @@ describe("URL generation", function () { it('should generate the correct URL for trim transformation when set to true (boolean)', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ trim: true }] @@ -602,7 +601,7 @@ describe("URL generation", function () { it('should generate the correct URL for trim transformation when provided as an empty string', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ trim: "" }] @@ -613,7 +612,7 @@ describe("URL generation", function () { it('should generate the correct URL for trim transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ trim: 5 }] @@ -625,7 +624,7 @@ describe("URL generation", function () { // Width parameter tests it('should generate the correct URL for width transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ width: 400 }] @@ -636,7 +635,7 @@ describe("URL generation", function () { it('should generate the correct URL for width transformation when provided with a string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ width: "400" }] @@ -647,7 +646,7 @@ describe("URL generation", function () { it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ width: "iw_div_2" }] @@ -659,7 +658,7 @@ describe("URL generation", function () { // Height parameter tests it('should generate the correct URL for height transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ height: 300 }] @@ -670,7 +669,7 @@ describe("URL generation", function () { it('should generate the correct URL for height transformation when provided with a string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ height: "300" }] @@ -681,7 +680,7 @@ describe("URL generation", function () { it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ height: "ih_mul_0.5" }] @@ -693,7 +692,7 @@ describe("URL generation", function () { // AspectRatio parameter tests it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aspectRatio: "4:3" }] @@ -704,7 +703,7 @@ describe("URL generation", function () { it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aspectRatio: "4_3" }] @@ -715,7 +714,7 @@ describe("URL generation", function () { it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ aspectRatio: "iar_div_2" }] @@ -727,7 +726,7 @@ describe("URL generation", function () { // Background parameter tests it('should generate the correct URL for background transformation when provided with a solid color', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ background: "FF0000" }] @@ -738,7 +737,7 @@ describe("URL generation", function () { it('should generate the correct URL for background transformation when provided with the blurred option', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ background: "blurred" }] @@ -749,7 +748,7 @@ describe("URL generation", function () { it('should generate the correct URL for background transformation when provided with the genfill option', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ background: "genfill" }] @@ -761,7 +760,7 @@ describe("URL generation", function () { // Crop parameter tests it('should generate the correct URL for crop transformation when provided with force value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ crop: "force" }] @@ -772,7 +771,7 @@ describe("URL generation", function () { it('should generate the correct URL for crop transformation when provided with at_max value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ crop: "at_max" }] @@ -784,7 +783,7 @@ describe("URL generation", function () { // CropMode parameter tests it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ cropMode: "pad_resize" }] @@ -795,7 +794,7 @@ describe("URL generation", function () { it('should generate the correct URL for cropMode transformation when provided with extract value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ cropMode: "extract" }] @@ -807,7 +806,7 @@ describe("URL generation", function () { // Focus parameter tests it('should generate the correct URL for focus transformation when provided with a string value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ focus: "center" }] @@ -818,7 +817,7 @@ describe("URL generation", function () { it('should generate the correct URL for focus transformation when face detection is specified', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ focus: "face" }] @@ -830,7 +829,7 @@ describe("URL generation", function () { // Quality parameter test it('should generate the correct URL for quality transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ quality: 80 }] @@ -842,7 +841,7 @@ describe("URL generation", function () { // Coordinate parameters tests it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ x: 10 }] @@ -853,7 +852,7 @@ describe("URL generation", function () { it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ y: 20 }] @@ -864,7 +863,7 @@ describe("URL generation", function () { it('should generate the correct URL for xCenter transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ xCenter: 30 }] @@ -875,7 +874,7 @@ describe("URL generation", function () { it('should generate the correct URL for yCenter transformation when provided with a number value', function () { const url = imagekit.url({ - path: "/test_path1.jpg", + src: "/test_path1.jpg", transformation: [{ yCenter: 40 }] @@ -887,7 +886,7 @@ describe("URL generation", function () { // This is done just to test how SDK constructs URL, the actual transformation is not valid. it('Including deprecated properties', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ height: 300, width: 400, @@ -927,7 +926,7 @@ describe("URL generation", function () { // This is done just to test how SDK constructs URL, the actual transformation is not valid it('should generate the correct URL when comprehensive transformations, including video and AI transformations, are applied', function () { const url = imagekit.url({ - path: "/test_path.jpg", + src: "/test_path.jpg", transformation: [{ height: 300, width: 400, diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 52f67b4..856b497 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -8,7 +8,7 @@ describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if text is missing', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "text" @@ -20,7 +20,7 @@ describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if input', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "image" @@ -32,7 +32,7 @@ describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if input', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "video" @@ -44,7 +44,7 @@ describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if input', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "subtitle" @@ -56,7 +56,7 @@ describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if color is missing', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "solidColor" @@ -68,7 +68,7 @@ describe("Overlay Transformation Test Cases", function () { it('Text overlay generates correct URL with encoded overlay text', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "text", @@ -81,7 +81,7 @@ describe("Overlay Transformation Test Cases", function () { it('Image overlay generates correct URL with input logo.png', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "image", @@ -94,7 +94,7 @@ describe("Overlay Transformation Test Cases", function () { it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { const url = imagekit.url({ - path: "/base-video.mp4", + src: "/base-video.mp4", transformation: [{ overlay: { type: "video", @@ -107,7 +107,7 @@ describe("Overlay Transformation Test Cases", function () { it("Subtitle overlay generates correct URL with input subtitle.srt", function () { const url = imagekit.url({ - path: "/base-video.mp4", + src: "/base-video.mp4", transformation: [{ overlay: { type: "subtitle", @@ -120,7 +120,7 @@ describe("Overlay Transformation Test Cases", function () { it("Solid color overlay generates correct URL with background color FF0000", function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [{ overlay: { type: "solidColor", @@ -133,7 +133,7 @@ describe("Overlay Transformation Test Cases", function () { it('Combined overlay transformations generate correct URL including nested overlays', function () { const url = imagekit.url({ - path: "/base-image.jpg", + src: "/base-image.jpg", transformation: [ { // Text overlay @@ -281,7 +281,7 @@ describe("Overlay encoding test cases", function () { it('Nested simple path, should use i instead of ie, handle slash properly', function () { const url = imagekit.url({ - path: "/medium_cafe_B1iTdD0C.jpg", + src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "image", @@ -294,7 +294,7 @@ describe("Overlay encoding test cases", function () { it('Nested non-simple path, should use ie instead of i', function () { const url = imagekit.url({ - path: "/medium_cafe_B1iTdD0C.jpg", + src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "image", @@ -307,7 +307,7 @@ describe("Overlay encoding test cases", function () { it('Simple text overlay, should use i instead of ie', function () { const url = imagekit.url({ - path: "/medium_cafe_B1iTdD0C.jpg", + src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "text", @@ -320,7 +320,7 @@ describe("Overlay encoding test cases", function () { it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { const url = imagekit.url({ - path: "/medium_cafe_B1iTdD0C.jpg", + src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "text", @@ -333,7 +333,7 @@ describe("Overlay encoding test cases", function () { it('Non simple text overlay, should use ie instead of i', function () { const url = imagekit.url({ - path: "/medium_cafe_B1iTdD0C.jpg", + src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { type: "text", @@ -346,7 +346,7 @@ describe("Overlay encoding test cases", function () { it('Text overlay with explicit plain encoding', function () { const url = imagekit.url({ - path: "/sample.jpg", + src: "/sample.jpg", transformation: [{ overlay: { type: "text", @@ -360,7 +360,7 @@ describe("Overlay encoding test cases", function () { it('Text overlay with explicit base64 encoding', function () { const url = imagekit.url({ - path: "/sample.jpg", + src: "/sample.jpg", transformation: [{ overlay: { type: "text", @@ -374,7 +374,7 @@ describe("Overlay encoding test cases", function () { it('Image overlay with explicit plain encoding', function () { const url = imagekit.url({ - path: "/sample.jpg", + src: "/sample.jpg", transformation: [{ overlay: { type: "image", @@ -388,7 +388,7 @@ describe("Overlay encoding test cases", function () { it('Image overlay with explicit base64 encoding', function () { const url = imagekit.url({ - path: "/sample.jpg", + src: "/sample.jpg", transformation: [{ overlay: { type: "image", @@ -402,7 +402,7 @@ describe("Overlay encoding test cases", function () { it('Video overlay with explicit base64 encoding', function () { const url = imagekit.url({ - path: "/sample.mp4", + src: "/sample.mp4", transformation: [{ overlay: { type: "video", @@ -416,7 +416,7 @@ describe("Overlay encoding test cases", function () { it('Subtitle overlay with explicit plain encoding', function () { const url = imagekit.url({ - path: "/sample.mp4", + src: "/sample.mp4", transformation: [{ overlay: { type: "subtitle", @@ -430,7 +430,7 @@ describe("Overlay encoding test cases", function () { it('Subtitle overlay with explicit base64 encoding', function () { const url = imagekit.url({ - path: "/sample.mp4", + src: "/sample.mp4", transformation: [{ overlay: { type: "subtitle", @@ -444,7 +444,7 @@ describe("Overlay encoding test cases", function () { it("Avoid double encoding when transformation string is in query params", function () { const url = imagekit.url({ - path: "/sample.jpg", + src: "/sample.jpg", transformation: [{ overlay: { type: "text", From b15ed766d3c994af671cd330aa4e4d17aaffaea8 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 3 Apr 2025 17:19:43 +0530 Subject: [PATCH 108/166] refactor: update test descriptions to use 'src' instead of 'path' --- test/url-generation/basic.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index cdc57d2..a64dbc3 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -9,7 +9,7 @@ describe("URL generation", function () { var imagekit = new ImageKit(initializationParams); - it('should return an empty string when neither path nor src is provided', function () { + it('should return an empty string when src is not provided', function () { const url = imagekit.url({}); expect(url).equal(""); @@ -21,7 +21,7 @@ describe("URL generation", function () { expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); }); - it('should generate a valid URL when a path is provided without transformation', function () { + it('should generate a valid URL when src is provided without transformation', function () { const url = imagekit.url({ src: "/test_path.jpg" }); @@ -78,7 +78,7 @@ describe("URL generation", function () { 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 a valid path and transformation', function () { + it('should generate the correct URL with a valid src and transformation', function () { const url = imagekit.url({ src: "/test_path.jpg", transformation: [{ @@ -117,7 +117,7 @@ describe("URL generation", function () { }); - it('should generate the correct URL with transformationPosition as query parameter when path is provided', function () { + it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { const url = imagekit.url({ src: "/test_path.jpg", transformationPosition: "query", From 74b3073ef9bc64455020236c8def7ec8ab7139a2 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 4 Apr 2025 17:51:46 +0530 Subject: [PATCH 109/166] refactor: update export types and remove unused ImageKitOptions interface --- README.md | 2 - rollup.config.js | 4 +- src/constants/errorMessages.ts | 41 +- src/index.ts | 93 +- src/interfaces/ImageKitOptions.ts | 18 - src/interfaces/UploadOptions.ts | 24 +- src/interfaces/UploadResponse.ts | 43 +- src/interfaces/UrlOptions.ts | 11 + src/interfaces/index.ts | 6 +- src/upload.ts | 265 ++++++ src/upload/index.ts | 179 ---- src/{url/builder.ts => url.ts} | 24 +- src/utils/transformation.ts | 6 +- test/initialization.js | 70 -- test/upload.js | 107 +-- test/url-generation/basic.js | 1414 +++++++++++++++++------------ test/url-generation/overlay.js | 121 ++- 17 files changed, 1361 insertions(+), 1067 deletions(-) delete mode 100644 src/interfaces/ImageKitOptions.ts create mode 100644 src/upload.ts delete mode 100644 src/upload/index.ts rename src/{url/builder.ts => url.ts} (91%) delete mode 100644 test/initialization.js diff --git a/README.md b/README.md index d522861..1634536 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,6 @@ var imagekit = new ImageKit({ }); ``` -> Note: Never include your private API key in client-side code. The SDK will throw an error if you do. - ### Initialization Options | Option | Description | Example | diff --git a/rollup.config.js b/rollup.config.js index 713494c..95ae3de 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -33,8 +33,8 @@ export default [ { input: "src/index.ts", output: [ - { file: pkg.main, format: "cjs", exports: "default" }, - { file: pkg.module, format: "es", exports: "default" }, + { file: pkg.main, format: "cjs", exports: "named" }, + { file: pkg.module, format: "es", exports: "named" }, ], plugins: [ nodeResolve({ extensions: [".ts"] }), diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 64d21f0..b5a3eb1 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -1,25 +1,24 @@ export default { - MANDATORY_INITIALIZATION_MISSING: { message: "Missing urlEndpoint during SDK initialization", help: "" }, - INVALID_TRANSFORMATION_POSITION: { message: "Invalid transformationPosition parameter", help: "" }, - PRIVATE_KEY_CLIENT_SIDE: { message: "privateKey should not be passed on the client side", help: "" }, - 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: "" }, - MISSING_AUTHENTICATION_ENDPOINT: { message: "Missing authentication endpoint for upload", help: "" }, - MISSING_PUBLIC_KEY: { message: "Missing public key for upload", help: "" }, - AUTH_ENDPOINT_TIMEOUT: { message: "The authenticationEndpoint you provided timed out in 60 seconds", help: "" }, - AUTH_ENDPOINT_NETWORK_ERROR: { message: "Request to authenticationEndpoint failed due to network error", help: "" }, - AUTH_INVALID_RESPONSE: { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, + MANDATORY_INITIALIZATION_MISSING: { message: "Missing urlEndpoint during SDK initialization" }, + INVALID_TRANSFORMATION_POSITION: { message: "Invalid transformationPosition parameter" }, + PRIVATE_KEY_CLIENT_SIDE: { message: "privateKey should not be passed on the client side" }, + MISSING_UPLOAD_DATA: { message: "Missing data for upload" }, + MISSING_UPLOAD_FILE_PARAMETER: { message: "Missing file parameter for upload" }, + MISSING_UPLOAD_FILENAME_PARAMETER: { message: "Missing fileName parameter for upload" }, + MISSING_AUTHENTICATION_ENDPOINT: { message: "Missing authentication endpoint for upload" }, + MISSING_PUBLIC_KEY: { message: "Missing public key for upload" }, + AUTH_ENDPOINT_TIMEOUT: { message: "The authenticationEndpoint you provided timed out in 60 seconds" }, + AUTH_ENDPOINT_NETWORK_ERROR: { message: "Request to authenticationEndpoint failed due to network error" }, + AUTH_INVALID_RESPONSE: { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire." }, UPLOAD_ENDPOINT_NETWORK_ERROR: { - message: "Request to ImageKit upload endpoint failed due to network error", - help: "", + message: "Request to ImageKit upload endpoint failed due to network error" }, - INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, - MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }, - MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }, - MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }, - 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: "" }, - UPLOAD_ABORTED: { message: "Request aborted by the user", help: "" }, + INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter" }, + MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication." }, + MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication." }, + MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication." }, + INVALID_TRANSFORMATION: { message: "Invalid transformation parameter. Please include at least pre, post, or both." }, + INVALID_PRE_TRANSFORMATION: { message: "Invalid pre transformation parameter." }, + INVALID_POST_TRANSFORMATION: { message: "Invalid post transformation parameter." }, + UPLOAD_ABORTED: { message: "Request aborted by the user" }, }; diff --git a/src/index.ts b/src/index.ts index e34687e..c43d292 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,88 +1,11 @@ -import errorMessages from "./constants/errorMessages"; -import { ImageKitOptions, UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; -import IKResponse from "./interfaces/IKResponse"; +import { UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; import { upload } from "./upload"; -import { buildURL } from "./url/builder"; -import transformationUtils from "./utils/transformation"; -type MakeRequired = Omit & Required>; +import { buildURL, generateTransformationString } from "./url"; +export { buildURL, generateTransformationString, upload }; -function mandatoryParametersAvailable(options: ImageKitOptions) { - return options.urlEndpoint; -} - -class ImageKit { - options: MakeRequired = { - publicKey: "", - urlEndpoint: "", - transformationPosition: transformationUtils.getDefault(), - }; - - constructor(opts: MakeRequired) { - this.options = { ...this.options, ...(opts || {}) }; - if (!mandatoryParametersAvailable(this.options)) { - throw errorMessages.MANDATORY_INITIALIZATION_MISSING; - } - - if (!transformationUtils.validParameters(this.options)) { - throw errorMessages.INVALID_TRANSFORMATION_POSITION; - } - } - - /** - * An instance method to generate URL for the given transformation parameters. This method is useful when you want to generate URL using the instance of the SDK without passing common parameters like `urlEndpoint` and `transformationPosition` every time. - */ - url(urlOptions: UrlOptions & Partial>): string { - // Merge the options with the instance options - const options = { - ...this.options, - ...urlOptions, - }; - return ImageKit.url(options); - } - - /** - * A static method to generate URL for the given transformation parameters. This method is useful when you want to generate URL without creating an instance of the SDK. - */ - static url(urlOptions: UrlOptions & Required> & Pick): string { - return buildURL(urlOptions); - } - - /** - * An instance method to upload file to ImageKit.io. This method is useful when you want to upload file using the instance of the SDK without passing common parameters like `urlEndpoint` and `publicKey` every time. - */ - upload(uploadOptions: UploadOptions & Partial>): Promise> { - // Merge the options with the instance options - const options = { - ...this.options, - ...uploadOptions, - }; - - return ImageKit.upload(options as UploadOptions & Required>); - } - - /** - * A static method to upload file to ImageKit.io. This method is useful when you want to upload file without creating an instance of the SDK. - */ - static upload(uploadOptions: UploadOptions & Required>): Promise> { - if (!uploadOptions.publicKey || uploadOptions.publicKey.length === 0) { - throw errorMessages.MISSING_PUBLIC_KEY; - } - - const { xhr: userProvidedXHR } = uploadOptions || {}; - delete uploadOptions.xhr; - const xhr = userProvidedXHR || new XMLHttpRequest(); - - // Extract publicKey from uploadOptions - const { publicKey, ...rest } = uploadOptions; - return upload( - xhr, - rest, - { - publicKey, - } - ) - } -} - -export default ImageKit; \ No newline at end of file +export type { + UrlOptions, + UploadOptions, + UploadResponse +}; diff --git a/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts deleted file mode 100644 index 6f8602d..0000000 --- a/src/interfaces/ImageKitOptions.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TransformationPosition } from "."; - -export interface ImageKitOptions { - /** - * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). - */ - urlEndpoint?: string; - - /** - * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). - */ - publicKey?: string; - - /** - * By default, the transformation string is added as a query parameter in the URL e.g. `?tr=w-100,h-100`. If you want to add the transformation string in the path of the URL, set this to `path`. - */ - transformationPosition?: TransformationPosition; -} diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 9e5bd47..3b84411 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -16,7 +16,7 @@ interface AbsObject { type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; -interface Transformation{ +interface Transformation { pre?: string post?: PostTransformation[] } @@ -35,28 +35,38 @@ export interface UploadOptions { * Pass the full URL, for example - https://www.example.com/rest-of-the-image-path.jpg. */ file: string | Blob | File; + + /** + * The name with which the file has to be uploaded. + * The file name 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 _ + */ + fileName: string; + /** * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. This should be in lowercase. * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. */ signature: string; + /** * A unique value generated by the client, which will be used by the ImageKit.io server 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. * 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; + /** * The time until your signature is valid. It must be a Unix time in less than 1 hour into the future. It should be in seconds. */ expire: number; + /** - * The name with which the file has to be uploaded. - * The file name 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 _ + * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). */ - fileName: string; + publicKey: string; + /** * Whether to use a unique filename for this file or not. * - Accepts true or false. diff --git a/src/interfaces/UploadResponse.ts b/src/interfaces/UploadResponse.ts index b38cf27..963d2c0 100644 --- a/src/interfaces/UploadResponse.ts +++ b/src/interfaces/UploadResponse.ts @@ -94,8 +94,14 @@ export interface Metadata { }; } +export interface ResponseMetadata { + statusCode: number; + requestId: string; + headers: Record; +} + /** - * Response from uploading a file + * Response from server when file is uploaded successfully. * * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Responses} */ @@ -103,39 +109,39 @@ 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; + fileId?: string; /** * The name of the uploaded file. */ - name: string; + name?: string; /** * The URL of the file. */ - url: string; + url?: string; /** * In case of an image, a small thumbnail URL. */ - thumbnailUrl: string; + thumbnailUrl?: string; /** * Height of the uploaded image file. Only applicable when file type is image. */ - height: number; + height?: number; /** * Width of the uploaded image file. Only applicable when file type is image. */ - width: number; + width?: number; /** * Size of the uploaded file in bytes. */ - size: number; + size?: number; /** * Type of file. It can either be image or non-image. */ - fileType: FileType; + fileType?: FileType; /** * The path of the file uploaded. It includes any folder that you specified while uploading. */ - filePath: string; + filePath?: string; /** * Array of tags associated with the image. */ @@ -143,11 +149,11 @@ export interface UploadResponse { /** * Is the file marked as private. It can be either true or false. */ - isPrivateFile: boolean; + isPrivateFile?: boolean; /** * Value of custom coordinates associated with the image in format x,y,width,height. */ - customCoordinates: string | null; + customCoordinates?: string | null; /** * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. */ @@ -156,8 +162,21 @@ export interface UploadResponse { * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. */ AITags?: object[]; + /* * Field object which will contain the status of each extension at the time of completion of the update/upload request. */ extensionStatus?: { [key: string]: string } + + /** + * Message indicating that the file upload is accepted. This field is only present when the upload is accepted but not yet processed. + * This can happen when the file is being processed for pre-transformation for video. + * The upload will be completed once the pre-transformation is done. + */ + message?: string + + /** + * Response metadata for debugging purposes. + */ + readonly $ResponseMetadata: ResponseMetadata; } diff --git a/src/interfaces/UrlOptions.ts b/src/interfaces/UrlOptions.ts index 728f683..ff88bed 100644 --- a/src/interfaces/UrlOptions.ts +++ b/src/interfaces/UrlOptions.ts @@ -1,4 +1,5 @@ import { Transformation } from "./Transformation"; +import { TransformationPosition } from "."; export interface UrlOptions { /** @@ -6,6 +7,11 @@ export interface UrlOptions { */ src: string; + /** + * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + */ + urlEndpoint: string; + /** * An array of objects specifying the transformations to be applied in the URL. If more than one transformation is specified, they are applied in the order they are specified as chained transformations. * @@ -19,4 +25,9 @@ export interface UrlOptions { * Especially useful, if you want to add some versioning parameter to your URLs. */ queryParameters?: { [key: string]: string | number }; + + /** + * By default, the transformation string is added as a query parameter in the URL e.g. `?tr=w-100,h-100`. If you want to add the transformation string in the path of the URL, set this to `path`. + */ + transformationPosition?: TransformationPosition; } \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 50afe5c..71a6ec6 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,7 +1,7 @@ -import { ImageKitOptions } from "./ImageKitOptions"; import { TransformationPosition } from "./Transformation"; import { UploadOptions } from "./UploadOptions"; -import { UploadResponse, FileType } from "./UploadResponse"; +import { FileType, UploadResponse } from "./UploadResponse"; import { UrlOptions } from "./UrlOptions"; -export type { ImageKitOptions, TransformationPosition, UploadOptions, UploadResponse, FileType, UrlOptions }; +export type { FileType, TransformationPosition, UploadOptions, UploadResponse, UrlOptions }; + diff --git a/src/upload.ts b/src/upload.ts new file mode 100644 index 0000000..cab0d69 --- /dev/null +++ b/src/upload.ts @@ -0,0 +1,265 @@ +import errorMessages from "./constants/errorMessages"; +import { UploadOptions } from "./interfaces/UploadOptions"; +import { ResponseMetadata, UploadResponse } from "./interfaces/UploadResponse"; + + +export class ImageKitInvalidRequestError extends Error { + /** + * Optional metadata about the response. It is only available if server returns a response. + */ + readonly $ResponseMetadata?: ResponseMetadata; + constructor(message: string, responseMetadata?: ResponseMetadata) { + super(message); + this.name = "ImageKitInvalidRequestError"; + this.$ResponseMetadata = responseMetadata; + } +} + +export class ImageKitAbortError extends Error { + /** + * The reason why the operation was aborted, which can be any JavaScript value. If not specified, the reason is set to "AbortError" DOMException. + */ + reason?: unknown; + constructor(message: string, reason?: unknown) { + super(message); + this.name = "ImageKitAbortError"; + this.reason = reason; + } +} + +export class ImageKitUploadNetworkError extends Error { + constructor(message: string) { + super(message); + this.name = "ImageKitUploadNetworkError"; + } +} + +export class ImageKitServerError extends Error { + /** + * Optional metadata about the response. It is only available if server returns a response. + */ + readonly $ResponseMetadata?: ResponseMetadata; + constructor(message: string, responseMetadata?: ResponseMetadata) { + super(message); + this.name = "ImageKitServerError"; + this.$ResponseMetadata = responseMetadata; + } +} + +/** + * Uploads a file with the given upload options. + * + * @throws {ImageKitInvalidRequestError} If the request is invalid. + * @throws {ImageKitAbortError} If the request is aborted. + * @throws {ImageKitUploadNetworkError} If there is a network error. + * @throws {ImageKitServerError} If there is a server error. + * + * @param {UploadOptions} uploadOptions - The options for uploading the file. + * @returns A Promise resolving to a successful {@link UploadResponse} + */ +export const upload = ( + uploadOptions: UploadOptions +): Promise => { + if(!uploadOptions) { + return Promise.reject(new ImageKitInvalidRequestError("Invalid options provided for upload")); + } + return new Promise((resolve, reject) => { + const { xhr: userProvidedXHR } = uploadOptions || {}; + delete uploadOptions.xhr; + const xhr = userProvidedXHR || new XMLHttpRequest(); + + if (!uploadOptions.file) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILE_PARAMETER.message)); + } + + if (!uploadOptions.fileName) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER.message)); + } + + if (!uploadOptions.publicKey || uploadOptions.publicKey.length === 0) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_PUBLIC_KEY.message)); + } + + if (!uploadOptions.token) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_TOKEN.message)); + } + + if (!uploadOptions.signature) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_SIGNATURE.message)); + } + + if (!uploadOptions.expire) { + return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_EXPIRE.message)); + } + + if (uploadOptions.transformation) { + if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { + return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_TRANSFORMATION.message)); + } + if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { + return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_PRE_TRANSFORMATION.message)); + } + 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)) { + return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); + } else if (transformation.type === "transformation" && !transformation.value) { + return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); + } + } + } else { + return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); + } + } + } + + var formData = new FormData(); + let key: keyof typeof uploadOptions; + for (key in uploadOptions) { + if (key) { + if (key === "file" && typeof uploadOptions.file != "string") { + formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); + } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { + formData.append('tags', uploadOptions.tags.join(",")); + } else if (key === 'signature') { + formData.append("signature", uploadOptions.signature); + } else if (key === 'expire') { + formData.append("expire", String(uploadOptions.expire)); + } else if (key === 'token') { + formData.append("token", uploadOptions.token); + } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { + formData.append('responseFields', uploadOptions.responseFields.join(",")); + } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { + formData.append('extensions', JSON.stringify(uploadOptions.extensions)); + } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && + !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { + formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); + } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && + uploadOptions.transformation !== null) { + formData.append(key, JSON.stringify(uploadOptions.transformation)); + } else if (key === 'checks' && uploadOptions.checks) { + formData.append("checks", uploadOptions.checks); + } else if (uploadOptions[key] !== undefined) { + if (["onProgress", "signal"].includes(key)) continue; + formData.append(key, String(uploadOptions[key])); + } + } + } + + formData.append("publicKey", uploadOptions.publicKey); + + if (uploadOptions.onProgress) { + xhr.upload.onprogress = function (event: ProgressEvent) { + if (uploadOptions.onProgress) uploadOptions.onProgress(event) + }; + } + + function onAbortHandler() { + xhr.abort(); + return reject(new ImageKitAbortError( + "Upload aborted", + // @ts-ignore for TypeScript versions lacking `signal.reason` + uploadOptions.signal?.reason + )); + } + + if (uploadOptions.signal) { + if (uploadOptions.signal.aborted) { + // If the signal is already aborted, return immediately with the reason + + return reject(new ImageKitAbortError( + "Upload aborted", + // @ts-ignore for TypeScript versions lacking `signal.reason` + uploadOptions.signal?.reason + )); + } + + // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted + uploadOptions.signal.addEventListener("abort", onAbortHandler); + + // On XHR completion (success, fail, or abort), remove just this abort handler + xhr.addEventListener("loadend", () => { + if (uploadOptions.signal) { + uploadOptions.signal.removeEventListener("abort", onAbortHandler); + } + }); + } + + xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); + xhr.onerror = function (e) { + return reject(new ImageKitInvalidRequestError(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR.message)); + } + xhr.onload = function () { + if (xhr.status >= 200 && xhr.status < 300) { + try { + var body = JSON.parse(xhr.responseText); + var uploadResponse = addResponseHeadersAndBody(body, xhr); + return resolve(uploadResponse); + } catch (ex: any) { + return reject(ex); + } + } else if (xhr.status >= 400 && xhr.status < 500) { + // Send ImageKitInvalidRequestError + try { + var body = JSON.parse(xhr.responseText); + return reject(new ImageKitInvalidRequestError( + body.message, + getResponseMetadata(xhr) + )); + } catch (ex: any) { + return reject(ex); + } + } else { + // Send ImageKitServerError + try { + var body = JSON.parse(xhr.responseText); + return reject(new ImageKitServerError( + "Server error occurred while uploading the file. This is rare and usually temporary.", + getResponseMetadata(xhr) + )); + } catch (ex: any) { + return reject(ex); + } + } + }; + xhr.send(formData); + }); +}; + + +const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest) => { + let response = { ...body }; + const responseMetadata = getResponseMetadata(xhr); + Object.defineProperty(response, "$ResponseMetadata", { + value: responseMetadata, + enumerable: false, + writable: false + }); + return response; +} + +const getResponseMetadata = (xhr: XMLHttpRequest): ResponseMetadata => { + const headers = getResponseHeaderMap(xhr); + const responseMetadata = { + statusCode: xhr.status, + headers: headers, + requestId: headers["x-request-id"] + } + return responseMetadata; +} + +function getResponseHeaderMap(xhr: XMLHttpRequest): Record { + const headers: Record = {}; + const responseHeaders = xhr.getAllResponseHeaders(); + if (Object.keys(responseHeaders).length) { + responseHeaders + .trim() + .split(/[\r\n]+/) + .map(value => value.split(/: /)) + .forEach(keyValue => { + headers[keyValue[0].trim().toLowerCase()] = keyValue[1].trim(); + }); + } + return headers; +} diff --git a/src/upload/index.ts b/src/upload/index.ts deleted file mode 100644 index ea8b55e..0000000 --- a/src/upload/index.ts +++ /dev/null @@ -1,179 +0,0 @@ -import errorMessages from "../constants/errorMessages"; -import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; -import IKResponse from "../interfaces/IKResponse"; - -export const upload = ( - xhr: XMLHttpRequest, - uploadOptions: UploadOptions, - options: Required> -): Promise> => { - return new Promise((resolve, reject) => { - if (!uploadOptions.file) { - return reject(errorMessages.MISSING_UPLOAD_FILE_PARAMETER) - } - - if (!uploadOptions.fileName) { - return reject(errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER); - } - - if (!options.publicKey) { - return reject(errorMessages.MISSING_PUBLIC_KEY); - } - - if (!uploadOptions.token) { - return reject(errorMessages.MISSING_TOKEN); - } - - if (!uploadOptions.signature) { - return reject(errorMessages.MISSING_SIGNATURE); - } - - if (!uploadOptions.expire) { - return reject(errorMessages.MISSING_EXPIRE); - } - - if (uploadOptions.transformation) { - if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { - return reject(errorMessages.INVALID_TRANSFORMATION); - } - if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { - return reject(errorMessages.INVALID_PRE_TRANSFORMATION); - 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)) { - return reject(errorMessages.INVALID_POST_TRANSFORMATION); - } else if (transformation.type === "transformation" && !transformation.value) { - return reject(errorMessages.INVALID_POST_TRANSFORMATION); - } - } - } else { - return reject(errorMessages.INVALID_POST_TRANSFORMATION); - } - } - } - - var formData = new FormData(); - let key: keyof typeof uploadOptions; - for (key in uploadOptions) { - if (key) { - if (key === "file" && typeof uploadOptions.file != "string") { - formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); - } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { - formData.append('tags', uploadOptions.tags.join(",")); - } else if (key === 'signature') { - formData.append("signature", uploadOptions.signature); - } else if (key === 'expire') { - formData.append("expire", String(uploadOptions.expire)); - } else if (key === 'token') { - formData.append("token", uploadOptions.token); - } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { - formData.append('responseFields', uploadOptions.responseFields.join(",")); - } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { - formData.append('extensions', JSON.stringify(uploadOptions.extensions)); - } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && - !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { - formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); - } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && - uploadOptions.transformation !== null) { - formData.append(key, JSON.stringify(uploadOptions.transformation)); - } else if (key === 'checks' && uploadOptions.checks) { - formData.append("checks", uploadOptions.checks); - } else if (uploadOptions[key] !== undefined) { - if (["onProgress", "signal"].includes(key)) continue; - formData.append(key, String(uploadOptions[key])); - } - } - } - - formData.append("publicKey", options.publicKey); - - if (uploadOptions.onProgress) { - xhr.upload.onprogress = function (event: ProgressEvent) { - if (uploadOptions.onProgress) uploadOptions.onProgress(event) - }; - } - - function onAbortHandler() { - xhr.abort(); - // Provide the reason or fallback error - // @ts-ignore for TypeScript versions lacking `signal.reason` - return reject(uploadOptions.signal?.reason ?? errorMessages.UPLOAD_ABORTED); - } - - if (uploadOptions.signal) { - if (uploadOptions.signal.aborted) { // If the signal is already aborted, return immediately with the reason - // @ts-ignore for TypeScript versions lacking `signal.reason` - return reject(uploadOptions.signal.reason ?? errorMessages.UPLOAD_ABORTED); - } - - // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted - uploadOptions.signal.addEventListener("abort", onAbortHandler); - - // On XHR completion (success, fail, or abort), remove just this abort handler - xhr.addEventListener("loadend", () => { - if (uploadOptions.signal) { - uploadOptions.signal.removeEventListener("abort", onAbortHandler); - } - }); - } - - xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); - xhr.onerror = function (e) { - return reject(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR); - } - xhr.onload = function () { - if (xhr.status === 200) { - try { - var body = JSON.parse(xhr.responseText); - var uploadResponse = addResponseHeadersAndBody(body, xhr); - return resolve(uploadResponse); - } catch (ex: any) { - return reject(ex); - } - } else { - try { - var body = JSON.parse(xhr.responseText); - var uploadError = addResponseHeadersAndBody(body, xhr); - return reject(uploadError) - } catch (ex: any) { - return reject(ex); - } - } - }; - xhr.send(formData); - }); - -}; - - -const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse => { - let response = { ...body }; - const responseMetadata = { - statusCode: xhr.status, - headers: getResponseHeaderMap(xhr) - } - Object.defineProperty(response, "$ResponseMetadata", { - value: responseMetadata, - enumerable: false, - writable: false - }); - return response as IKResponse; -} - -function getResponseHeaderMap(xhr: XMLHttpRequest) { - const headers: Record = {}; - const responseHeaders = xhr.getAllResponseHeaders(); - if (Object.keys(responseHeaders).length) { - responseHeaders - .trim() - .split(/[\r\n]+/) - .map(value => value.split(/: /)) - .forEach(keyValue => { - headers[keyValue[0].trim()] = keyValue[1].trim(); - }); - } - return headers; -} \ No newline at end of file diff --git a/src/url/builder.ts b/src/url.ts similarity index 91% rename from src/url/builder.ts rename to src/url.ts index fe8fe09..a52c44c 100644 --- a/src/url/builder.ts +++ b/src/url.ts @@ -1,6 +1,7 @@ -import { ImageKitOptions, UrlOptions } from "../interfaces"; -import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; -import transformationUtils, { safeBtoa } from "../utils/transformation"; +import { UrlOptions } from "./interfaces"; +import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; +import transformationUtils from "./utils/transformation"; +import { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. @@ -25,10 +26,11 @@ function pathJoin(parts: string[], sep?: string) { return parts.join(separator).replace(replace, separator); } -export const buildURL = (opts: UrlOptions & Required> & Pick) => { +export const buildURL = (opts: UrlOptions) => { opts.urlEndpoint = opts.urlEndpoint || ""; opts.src = opts.src || ""; - + opts.transformationPosition = opts.transformationPosition || "query"; + if (!opts.src) { return ""; } @@ -62,7 +64,7 @@ export const buildURL = (opts: UrlOptions & Required { return DEFAULT_TRANSFORMATION_POSITION; }, - addAsQueryParameter: (options: ImageKitOptions) => { + addAsQueryParameter: (options: UrlOptions) => { return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; }, - validParameters: (options: ImageKitOptions) => { + validParameters: (options: UrlOptions) => { if (typeof options.transformationPosition == "undefined") return false; return VALID_TRANSFORMATION_POSITIONS.indexOf(options.transformationPosition) != -1; }, diff --git a/test/initialization.js b/test/initialization.js deleted file mode 100644 index b3695ed..0000000 --- a/test/initialization.js +++ /dev/null @@ -1,70 +0,0 @@ -const chai = require("chai"); -const expect = chai.expect; -const initializationParams = require("./data").initializationParams -import ImageKit from "../src/index"; - - -describe("Initialization checks", function () { - var imagekit = new ImageKit(initializationParams); - - it('should throw error: options - empty object', function () { - try { - new ImageKit({}); - } catch(err) { - expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); - } - }); - - it('should throw error: options - undefined', function () { - try { - new ImageKit(); - } catch(err) { - expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); - } - }); - - it('Pass private Key', function () { - try { - new ImageKit({ - urlEndpoint: initializationParams.urlEndpoint, - privateKey: "should_not_pass" - }); - } catch(err) { - expect(err.message).to.be.equal('privateKey should not be passed on the client side'); - } - }); - - 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); - }); - - 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'); - }); - - it('only urlEndpoint is required parameter', function () { - let imagekit = new ImageKit({ - urlEndpoint: initializationParams.urlEndpoint - }); - - expect(imagekit.options).to.be.an('object'); - expect(imagekit.options).to.have.property('urlEndpoint').to.be.equal(initializationParams.urlEndpoint); - expect(imagekit.url).to.exist.and.to.be.a('function'); - expect(imagekit.upload).to.exist.and.to.be.a('function'); - - }); - - it('should throw error: invalid transformationPosition', function () { - try { - new ImageKit({...initializationParams, transformationPosition: "test"}); - } catch(err) { - expect(err.message).to.be.equal('Invalid transformationPosition parameter'); - } - }); -}); \ No newline at end of file diff --git a/test/upload.js b/test/upload.js index 0a9978a..ceee2c0 100644 --- a/test/upload.js +++ b/test/upload.js @@ -1,9 +1,9 @@ const chai = require("chai"); const sinon = require("sinon"); const expect = chai.expect; -const initializationParams = require("./data").initializationParams; import 'regenerator-runtime/runtime'; -import ImageKit from "../src/index"; +import { upload } from "../src/index"; +import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError } from '../src/upload'; var requests, server; const uploadSuccessResponseObj = { @@ -26,7 +26,8 @@ const uploadSuccessResponseObj = { const securityParameters = { signature: "test_signature", expire: 123, - token: "test_token" + token: "test_token", + publicKey: "test_public_key", }; function successUploadResponse() { @@ -61,10 +62,7 @@ async function sleep(ms = 0) { }); } -describe("File upload", function () { - - var imagekit = new ImageKit(initializationParams); - +describe("File upload", async function () { beforeEach(() => { global.XMLHttpRequest = sinon.useFakeXMLHttpRequest(); requests = []; @@ -79,10 +77,11 @@ describe("File upload", function () { it('Invalid options', async function () { try { - await imagekit.upload(undefined); + await upload(); throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ help: "", message: "Missing file parameter for upload" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid options provided for upload"); } }); @@ -92,13 +91,14 @@ describe("File upload", function () { file: "https://ik.imagekit.io/remote-url.jpg" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); try { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ help: "", message: "Missing fileName parameter for upload" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing fileName parameter for upload"); } }); @@ -108,13 +108,14 @@ describe("File upload", function () { fileName: "test_file_name", }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); try { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ help: "", message: "Missing file parameter for upload" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing file parameter for upload"); } }); @@ -123,16 +124,18 @@ describe("File upload", function () { fileName: "test_file_name", file: "test_file", signature: 'test_signature', - expire: 123 + expire: 123, + publicKey: 'test_public_key' }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); try { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing token for upload. The SDK expects token, signature and expire for authentication."); } }); @@ -144,7 +147,7 @@ describe("File upload", function () { expire: 123 }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); try { await uploadPromise; @@ -162,7 +165,7 @@ describe("File upload", function () { signature: 'test_signature' }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); try { await uploadPromise; @@ -198,7 +201,7 @@ describe("File upload", function () { file: "test_file" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); // Simulate network error on upload API @@ -224,7 +227,7 @@ describe("File upload", function () { isPrivateFile: true }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -257,7 +260,7 @@ describe("File upload", function () { isPrivateFile: true }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -287,7 +290,7 @@ describe("File upload", function () { isPrivateFile: true }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -318,7 +321,7 @@ describe("File upload", function () { tags: ["test_tag1", "test_tag2"] }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -360,7 +363,7 @@ describe("File upload", function () { ], webhookUrl: "https://your-domain/?appId=some-id" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -394,7 +397,7 @@ describe("File upload", function () { tags: undefined }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -425,7 +428,7 @@ describe("File upload", function () { file: buffer }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -456,7 +459,7 @@ describe("File upload", function () { file: "test_file" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -480,7 +483,7 @@ describe("File upload", function () { file: "test_file" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -507,7 +510,7 @@ describe("File upload", function () { file: "test_file" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", @@ -534,7 +537,7 @@ describe("File upload", function () { file: "https://ik.imagekit.io/remote-url.jpg" }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -566,7 +569,7 @@ describe("File upload", function () { file: "https://ik.imagekit.io/remote-url.jpg" }; - const uploadPromise = imagekit.upload({ + const uploadPromise = upload({ ...fileOptions, publicKey: newPublicKey }); @@ -613,7 +616,7 @@ describe("File upload", function () { overwriteTags: false, overwriteCustomMetadata: false }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -664,7 +667,7 @@ describe("File upload", function () { color: "red" }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -716,7 +719,7 @@ describe("File upload", function () { color: "red" }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -764,7 +767,7 @@ describe("File upload", function () { ], xhr }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); expect(server.requests[0]).to.be.equal(xhr); expect(server.requests[0].onprogress.toString()).to.be.equal(fun.toString()); @@ -806,7 +809,7 @@ describe("File upload", function () { ] }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); @@ -851,7 +854,7 @@ describe("File upload", function () { }; try { - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); await sleep(); errorUploadResponse(500, errRes); await sleep(); @@ -880,7 +883,7 @@ describe("File upload", function () { ], xhr }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); @@ -933,7 +936,7 @@ describe("File upload", function () { ] }; - var uploadPromise = imagekit.upload(fileOptions) + var uploadPromise = upload(fileOptions) expect(server.requests.length).to.be.equal(1); await sleep(); @@ -972,7 +975,7 @@ describe("File upload", function () { customMetadata: undefined }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1008,7 +1011,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1036,7 +1039,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "w-100" }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1064,7 +1067,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "transformation", value: "w-100" }] }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1092,7 +1095,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: {}, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1118,7 +1121,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { pre: "" }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1144,7 +1147,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "abs", value: "" }] }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1170,7 +1173,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: [{ type: "transformation", value: "" }] }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1196,7 +1199,7 @@ describe("File upload", function () { useUniqueFileName: false, transformation: { post: {} }, }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { @@ -1222,7 +1225,7 @@ describe("File upload", function () { useUniqueFileName: false, checks: "'request.folder' : '/'", }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); successUploadResponse(); @@ -1248,7 +1251,7 @@ describe("File upload", function () { file: "test_file", onProgress: progressSpy }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); server.requests[0].uploadProgress({ lengthComputable: true, loaded: 50, total: 100 }); @@ -1269,7 +1272,7 @@ describe("File upload", function () { file: "test_file", signal: abortController.signal }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); abortController.abort(); await sleep(); @@ -1289,7 +1292,7 @@ describe("File upload", function () { file: "test_file", signal: abortController.signal }; - const uploadPromise = imagekit.upload(fileOptions); + const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); abortController.abort("abort reason"); await sleep(); diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index a64dbc3..aa3200d 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -1,28 +1,31 @@ const chai = require("chai"); -const pkg = require("../../package.json"); -global.FormData = require('formdata-node'); const expect = chai.expect; -const initializationParams = require("../data").initializationParams -import ImageKit from "../../src/index"; +import { buildURL } from "../../src/index"; describe("URL generation", function () { - - var imagekit = new ImageKit(initializationParams); - it('should return an empty string when src is not provided', function () { - const url = imagekit.url({}); + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query" + }); expect(url).equal(""); }); it('should return an empty string for an invalid src URL', function () { - const url = imagekit.url({ src: "/" }); + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", + src: "/" + }); expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); }); it('should generate a valid URL when src is provided without transformation', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg" }); @@ -30,7 +33,9 @@ describe("URL generation", function () { }); it('should generate a valid URL when a src is provided without transformation', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" }); @@ -38,950 +43,1235 @@ describe("URL generation", function () { }); it('should generate a valid URL when undefined transformation parameters are provided with path', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path_alt.jpg", transformation: undefined, - transformationPosition: undefined, + transformationPosition: "query" }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); }); it("By default transformationPosition should be query", function () { - var imagekitNew = new ImageKit({ - publicKey: "test_public_key", + const url = buildURL({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - }); - const url = imagekitNew.url({ src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - rotation: 90 - }] + transformation: [ + { + height: "300", + width: "400" + }, + { + rotation: 90 + } + ] }); expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90"); }); it('should generate the URL without sdk version', function () { - const ik = new ImageKit(initializationParams) - - const url = ik.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] + transformation: [ + { + height: "300", + width: "400" + } + ], + transformationPosition: "path" }); 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 a valid src and transformation', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] + transformation: [ + { + height: "300", + width: "400" + } + ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg`); + // Now transformed URL goes into query since transformationPosition is "query". + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); }); it('should generate the correct URL when the provided path contains multiple leading slashes', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "///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`); + transformation: [ + { + height: "300", + width: "400" + } + ] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); }); it('should generate the correct URL when the urlEndpoint is overridden', function () { - const url = imagekit.url({ + const url = buildURL({ + // We do not override urlEndpoint here urlEndpoint: "https://ik.imagekit.io/test_url_endpoint_alt", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] - }) - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/tr:h-300,w-400/test_path.jpg`); + transformation: [ + { + height: "300", + width: "400" + } + ] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=h-300,w-400`); }); it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path.jpg", transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] + transformation: [ + { + height: "300", + width: "400" + } + ] }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); }); it('should generate the correct URL with a valid src parameter and transformation', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformation: [{ - "height": "300", - "width": "400" - }] + transformation: [ + { + height: "300", + width: "400" + } + ] }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); }); it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", transformationPosition: "query", - transformation: [{ - "height": "300", - "width": "400" - }] + transformation: [ + { + height: "300", + width: "400" + } + ] }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); }); it('should merge query parameters correctly in the generated URL', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", queryParameters: { t2: "v2", t3: "v3" }, - transformation: [{ - "height": "300", - "width": "400" - }] + 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,w-400`); }); - it('should generate the correct URL with chained transformations', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rt": "90" - }] - }) + 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90`); }); - it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400" - }, { - "rndm_trnsf": "abcd" - }] - }) + 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rndm_trnsf-abcd`); }); it('should generate the correct URL when overlay image transformation is provided', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) + 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end`); }); it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - "raw": "l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end" - }] - }) + 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end`); }); it('should generate the correct URL when border transformation is applied', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "height": "300", - "width": "400", - border: "20_FF0000" - }] - }) + 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`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,b-20_FF0000`); }); it('should generate the correct URL when transformation has empty key and value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "": "" - }] - }) + transformation: [ + { + "": "" + } + ] + }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); }); - /** - * Provided to provide support to a new transform without sdk update - */ it('should generate the correct URL when an undefined transform is provided', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - "undefined-transform": "true" - }] - }) + transformation: [ + { + "undefined-transform": "true" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:undefined-transform-true/test_path.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=undefined-transform-true`); }); it('should generate the correct URL when transformation key has an empty value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - defaultImage: "" - }] - }) + transformation: [ + { + defaultImage: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-/test_path.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=di-`); }); it('should generate the correct URL when transformation key has \'-\' as its value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - contrastStretch: "-" - }] - }) + transformation: [ + { + contrastStretch: "-" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-contrast/test_path.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=e-contrast`); }); it('should skip transformation parameters that are undefined or null', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - quality: undefined, - contrastStretch: null - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + quality: undefined, + contrastStretch: null + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); }); it('should skip transformation parameters that are false', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - contrastStretch: false - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + contrastStretch: false + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); }); it('should include only the key when transformation value is an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - shadow: "" - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + shadow: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow`); }); it('should include both key and value when transformation parameter value is provided', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - shadow: "bl-15_st-40_x-10_y-N5" - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + shadow: "bl-15_st-40_x-10_y-N5" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5`); }); it('should generate the correct URL when trim transformation is set to true as a boolean', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - trim: true - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + trim: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); }); it('should generate the correct URL when trim transformation is set to true as a string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - defaultImage: "/test_path.jpg", - trim: "true" - }] - }) + transformation: [ + { + defaultImage: "/test_path.jpg", + trim: "true" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); }); it('should generate the correct URL for AI background removal when set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackground: true - }] - }) + transformation: [ + { + aiRemoveBackground: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); }); it('should generate the correct URL for AI background removal when \'true\' is provided as a string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackground: "true" - }] - }) + transformation: [ + { + aiRemoveBackground: "true" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); }); it('should not apply AI background removal when value is not true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackground: "false" - }] - }) + transformation: [ + { + aiRemoveBackground: "false" + } + ] + }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); }); it('should generate the correct URL for external AI background removal when set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackgroundExternal: true - }] - }) + transformation: [ + { + aiRemoveBackgroundExternal: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); }); it('should generate the correct URL for external AI background removal when \'true\' is provided as a string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackgroundExternal: "true" - }] - }) + transformation: [ + { + aiRemoveBackgroundExternal: "true" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); }); it('should not apply external AI background removal when value is not true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiRemoveBackgroundExternal: "false" - }] - }) + transformation: [ + { + aiRemoveBackgroundExternal: "false" + } + ] + }); expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); }); it('should generate the correct URL when gradient transformation is provided as a string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - gradient: "ld-top_from-green_to-00FF0010_sp-1" - }] - }) + transformation: [ + { + gradient: "ld-top_from-green_to-00FF0010_sp-1" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient-ld-top_from-green_to-00FF0010_sp-1/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1`); }); it('should generate the correct URL when gradient transformation is provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - gradient: "" - }] - }) + transformation: [ + { + gradient: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); }); it('should generate the correct URL when gradient transformation is set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - gradient: true - }] - }) + transformation: [ + { + gradient: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); }); it('should generate the correct URL when AI drop shadow transformation is set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiDropShadow: true - }] - }) + transformation: [ + { + aiDropShadow: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); }); it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiDropShadow: "" - }] - }) + transformation: [ + { + aiDropShadow: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); }); it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aiDropShadow: "az-45" - }] - }) + transformation: [ + { + aiDropShadow: "az-45" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow-az-45/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-az-45`); }); it('should generate the correct URL when shadow transformation is set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - shadow: true - }] - }) + transformation: [ + { + shadow: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); }); it('should generate the correct URL when shadow transformation is provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - shadow: "" - }] - }) + transformation: [ + { + shadow: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); }); it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - shadow: "bl-15_st-40_x-10_y-N5" - }] - }) + transformation: [ + { + shadow: "bl-15_st-40_x-10_y-N5" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow-bl-15_st-40_x-10_y-N5`); }); it('should generate the correct URL when sharpen transformation is set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - sharpen: true - }] - }) + transformation: [ + { + sharpen: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); }); it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - sharpen: "" - }] - }) + transformation: [ + { + sharpen: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); }); it('should generate the correct URL when sharpen transformation is provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - sharpen: 10 - }] - }) + transformation: [ + { + sharpen: 10 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen-10/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen-10`); }); it('should generate the correct URL when unsharpMask transformation is set to true', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - unsharpMask: true - }] - }) + transformation: [ + { + unsharpMask: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); }); it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - unsharpMask: "" - }] - }) + transformation: [ + { + unsharpMask: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); }); it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - unsharpMask: "2-2-0.8-0.024" - }] - }) + transformation: [ + { + unsharpMask: "2-2-0.8-0.024" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm-2-2-0.8-0.024/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm-2-2-0.8-0.024`); }); it('should generate the correct URL for trim transformation when set to true (boolean)', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - trim: true - }] - }) + transformation: [ + { + trim: true + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); }); it('should generate the correct URL for trim transformation when provided as an empty string', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - trim: "" - }] - }) + transformation: [ + { + trim: "" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); }); it('should generate the correct URL for trim transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - trim: 5 - }] - }) + transformation: [ + { + trim: 5 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-5/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-5`); }); // Width parameter tests it('should generate the correct URL for width transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - width: 400 - }] - }) + transformation: [ + { + width: 400 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); }); it('should generate the correct URL for width transformation when provided with a string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - width: "400" - }] - }) + transformation: [ + { + width: "400" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); }); it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - width: "iw_div_2" - }] - }) + transformation: [ + { + width: "iw_div_2" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-iw_div_2/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-iw_div_2`); }); // Height parameter tests it('should generate the correct URL for height transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - height: 300 - }] - }) + transformation: [ + { + height: 300 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); }); it('should generate the correct URL for height transformation when provided with a string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - height: "300" - }] - }) + transformation: [ + { + height: "300" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); }); it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - height: "ih_mul_0.5" - }] - }) + transformation: [ + { + height: "ih_mul_0.5" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-ih_mul_0.5/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-ih_mul_0.5`); }); // AspectRatio parameter tests it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aspectRatio: "4:3" - }] - }) + transformation: [ + { + aspectRatio: "4:3" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4:3/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3`); }); it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aspectRatio: "4_3" - }] - }) + transformation: [ + { + aspectRatio: "4_3" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4_3/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4_3`); }); it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - aspectRatio: "iar_div_2" - }] - }) + transformation: [ + { + aspectRatio: "iar_div_2" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-iar_div_2/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-iar_div_2`); }); // Background parameter tests it('should generate the correct URL for background transformation when provided with a solid color', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - background: "FF0000" - }] - }) + transformation: [ + { + background: "FF0000" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-FF0000/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-FF0000`); }); it('should generate the correct URL for background transformation when provided with the blurred option', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - background: "blurred" - }] - }) + transformation: [ + { + background: "blurred" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-blurred/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-blurred`); }); it('should generate the correct URL for background transformation when provided with the genfill option', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - background: "genfill" - }] - }) + transformation: [ + { + background: "genfill" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-genfill/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-genfill`); }); // Crop parameter tests it('should generate the correct URL for crop transformation when provided with force value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - crop: "force" - }] - }) + transformation: [ + { + crop: "force" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-force/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-force`); }); it('should generate the correct URL for crop transformation when provided with at_max value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - crop: "at_max" - }] - }) + transformation: [ + { + crop: "at_max" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-at_max/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-at_max`); }); // CropMode parameter tests it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - cropMode: "pad_resize" - }] - }) + transformation: [ + { + cropMode: "pad_resize" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-pad_resize/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-pad_resize`); }); it('should generate the correct URL for cropMode transformation when provided with extract value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - cropMode: "extract" - }] - }) + transformation: [ + { + cropMode: "extract" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-extract/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-extract`); }); // Focus parameter tests it('should generate the correct URL for focus transformation when provided with a string value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - focus: "center" - }] - }) + transformation: [ + { + focus: "center" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-center/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-center`); }); it('should generate the correct URL for focus transformation when face detection is specified', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - focus: "face" - }] - }) + transformation: [ + { + focus: "face" + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-face/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-face`); }); // Quality parameter test it('should generate the correct URL for quality transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - quality: 80 - }] - }) + transformation: [ + { + quality: 80 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:q-80/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80`); }); // Coordinate parameters tests it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - x: 10 - }] - }) + transformation: [ + { + x: 10 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:x-10/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=x-10`); }); it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - y: 20 - }] - }) + transformation: [ + { + y: 20 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:y-20/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=y-20`); }); it('should generate the correct URL for xCenter transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - xCenter: 30 - }] - }) + transformation: [ + { + xCenter: 30 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:xc-30/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=xc-30`); }); it('should generate the correct URL for yCenter transformation when provided with a number value', function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path1.jpg", - transformation: [{ - yCenter: 40 - }] - }) + transformation: [ + { + yCenter: 40 + } + ] + }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:yc-40/test_path1.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=yc-40`); }); - // This is done just to test how SDK constructs URL, the actual transformation is not valid. it('Including deprecated properties', function () { - const url = imagekit.url({ + // This is just testing how the SDK constructs the URL, not actual valid transformations. + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - height: 300, - width: 400, - aspectRatio: '4-3', - quality: 40, - crop: 'force', - cropMode: 'extract', - focus: 'left', - format: 'jpeg', - radius: 50, - 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, - sharpen: 10, - unsharpMask: "2-2-0.8-0.024", - contrastStretch: true, - grayscale: true, - shadow: 'bl-15_st-40_x-10_y-N5', - gradient: 'from-red_to-white', - original: true, - raw: "h-200,w-300,l-image,i-logo.png,l-end" - }] - }) - - expect(url).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,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); - }); - - // This is done just to test how SDK constructs URL, the actual transformation is not valid - it('should generate the correct URL when comprehensive transformations, including video and AI transformations, are applied', function () { - const url = imagekit.url({ + 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/", + dpr: 3, + sharpen: 10, + unsharpMask: "2-2-0.8-0.024", + contrastStretch: true, + grayscale: true, + shadow: "bl-15_st-40_x-10_y-N5", + gradient: "from-red_to-white", + original: true, + raw: "h-200,w-300,l-image,i-logo.png,l-end" + } + ] + }); + + expect(url).equal( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end` + ); + }); + + it('should generate the correct URL with many transformations, including video and AI transforms', function () { + // Example test with comprehensive transformations + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", src: "/test_path.jpg", - transformation: [{ - height: 300, - width: 400, - aspectRatio: '4-3', - quality: 40, - crop: 'force', - cropMode: 'extract', - focus: 'left', - format: 'jpeg', - radius: 50, - 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, - x: 10, - y: 20, - xCenter: 30, - yCenter: 40, - flip: "h", - opacity: 0.8, - zoom: 2, - // Video transformations - videoCodec: "h264", - audioCodec: "aac", - startOffset: 5, - endOffset: 15, - duration: 10, - streamingResolutions: ["1440", "1080"], - // AI transformations - grayscale: true, - aiUpscale: true, - aiRetouch: true, - aiVariation: true, - aiDropShadow: true, - aiChangeBackground: "prompt-car", - aiRemoveBackground: true, - contrastStretch: true, - shadow: 'bl-15_st-40_x-10_y-N5', - sharpen: 10, - unsharpMask: "2-2-0.8-0.024", - gradient: 'from-red_to-white', - original: true, - page: "2_4", - raw: "h-200,w-300,l-image,i-logo.png,l-end" - }] - }) - - expect(url).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,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end/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/", + dpr: 3, + x: 10, + y: 20, + xCenter: 30, + yCenter: 40, + flip: "h", + opacity: 0.8, + zoom: 2, + // Video transformations + videoCodec: "h264", + audioCodec: "aac", + startOffset: 5, + endOffset: 15, + duration: 10, + streamingResolutions: ["1440", "1080"], + // AI transformations + grayscale: true, + aiUpscale: true, + aiRetouch: true, + aiVariation: true, + aiDropShadow: true, + aiChangeBackground: "prompt-car", + aiRemoveBackground: true, + contrastStretch: true, + shadow: "bl-15_st-40_x-10_y-N5", + sharpen: 10, + unsharpMask: "2-2-0.8-0.024", + gradient: "from-red_to-white", + original: true, + page: "2_4", + raw: "h-200,w-300,l-image,i-logo.png,l-end" + } + ] + }); + + expect(url).equal( + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end` + ); }); }); diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 856b497..880f9c8 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -1,13 +1,14 @@ const chai = require("chai"); const expect = chai.expect; -const initializationParams = require("../data").initializationParams; -import ImageKit from "../../src/index"; +import { buildURL } from "../../src/index"; import { safeBtoa } from "../../src/utils/transformation"; + describe("Overlay Transformation Test Cases", function () { - const imagekit = new ImageKit(initializationParams); it('Ignore invalid values if text is missing', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -18,8 +19,10 @@ describe("Overlay Transformation Test Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); }); - it('Ignore invalid values if input', function () { - const url = imagekit.url({ + it('Ignore invalid values if input (image)', function () { + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -30,8 +33,10 @@ describe("Overlay Transformation Test Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); }); - it('Ignore invalid values if input', function () { - const url = imagekit.url({ + it('Ignore invalid values if input (video)', function () { + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -42,8 +47,10 @@ describe("Overlay Transformation Test Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); }); - it('Ignore invalid values if input', function () { - const url = imagekit.url({ + it('Ignore invalid values if input (subtitle)', function () { + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -54,8 +61,10 @@ describe("Overlay Transformation Test Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); }); - it('Ignore invalid values if color is missing', function () { - const url = imagekit.url({ + it('Ignore invalid values if color is missing (solidColor)', function () { + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -67,7 +76,9 @@ describe("Overlay Transformation Test Cases", function () { }); it('Text overlay generates correct URL with encoded overlay text', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -80,7 +91,9 @@ describe("Overlay Transformation Test Cases", function () { }); it('Image overlay generates correct URL with input logo.png', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -93,7 +106,9 @@ describe("Overlay Transformation Test Cases", function () { }); it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-video.mp4", transformation: [{ overlay: { @@ -106,7 +121,9 @@ describe("Overlay Transformation Test Cases", function () { }); it("Subtitle overlay generates correct URL with input subtitle.srt", function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-video.mp4", transformation: [{ overlay: { @@ -119,7 +136,9 @@ describe("Overlay Transformation Test Cases", function () { }); it("Solid color overlay generates correct URL with background color FF0000", function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [{ overlay: { @@ -132,7 +151,9 @@ describe("Overlay Transformation Test Cases", function () { }); it('Combined overlay transformations generate correct URL including nested overlays', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", transformation: [ { @@ -197,7 +218,7 @@ describe("Overlay Transformation Test Cases", function () { } }, { - // Video overlay. Just for url generation testing, you can't overlay a video on an image. + // Video overlay. Just for URL generation testing, you can't actually overlay a video on an image. overlay: { type: "video", input: "play-pause-loop.mp4", @@ -220,7 +241,7 @@ describe("Overlay Transformation Test Cases", function () { } }, { - // Subtitle overlay. Just for url generation testing, you can't overlay a subtitle on an image. + // Subtitle overlay. Just for URL generation testing, you can't actually overlay a subtitle on an image. overlay: { type: "subtitle", input: "subtitle.srt", @@ -268,19 +289,16 @@ describe("Overlay Transformation Test Cases", function () { ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`); }); }); - describe("Overlay encoding test cases", function () { - const imagekit = new ImageKit({ - ...initializationParams, - urlEndpoint: "https://ik.imagekit.io/demo", // Using real url to test correctness quickly by clicking link - }); - it('Nested simple path, should use i instead of ie, handle slash properly', function () { - const url = imagekit.url({ + const url = buildURL({ + // Using a different endpoint here, as we are checking for /demo + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { @@ -293,7 +311,9 @@ describe("Overlay encoding test cases", function () { }); it('Nested non-simple path, should use ie instead of i', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { @@ -306,7 +326,9 @@ describe("Overlay encoding test cases", function () { }); it('Simple text overlay, should use i instead of ie', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { @@ -319,7 +341,9 @@ describe("Overlay encoding test cases", function () { }); it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { @@ -332,7 +356,9 @@ describe("Overlay encoding test cases", function () { }); it('Non simple text overlay, should use ie instead of i', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", transformation: [{ overlay: { @@ -345,7 +371,9 @@ describe("Overlay encoding test cases", function () { }); it('Text overlay with explicit plain encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ overlay: { @@ -359,7 +387,9 @@ describe("Overlay encoding test cases", function () { }); it('Text overlay with explicit base64 encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ overlay: { @@ -373,7 +403,9 @@ describe("Overlay encoding test cases", function () { }); it('Image overlay with explicit plain encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ overlay: { @@ -387,7 +419,9 @@ describe("Overlay encoding test cases", function () { }); it('Image overlay with explicit base64 encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ overlay: { @@ -401,7 +435,9 @@ describe("Overlay encoding test cases", function () { }); it('Video overlay with explicit base64 encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", transformation: [{ overlay: { @@ -415,7 +451,9 @@ describe("Overlay encoding test cases", function () { }); it('Subtitle overlay with explicit plain encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", transformation: [{ overlay: { @@ -429,7 +467,9 @@ describe("Overlay encoding test cases", function () { }); it('Subtitle overlay with explicit base64 encoding', function () { - const url = imagekit.url({ + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", transformation: [{ overlay: { @@ -443,7 +483,8 @@ describe("Overlay encoding test cases", function () { }); it("Avoid double encoding when transformation string is in query params", function () { - const url = imagekit.url({ + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ overlay: { From 9341aa29ee5251903e8d0f7e2b05dc6d39c99495 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 4 Apr 2025 18:15:45 +0530 Subject: [PATCH 110/166] working upload test cases --- src/upload.ts | 11 +++-- test/upload.js | 111 +++++++++++++++++++++++++++++++------------------ 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/upload.ts b/src/upload.ts index cab0d69..eb578ef 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -188,7 +188,7 @@ export const upload = ( xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); xhr.onerror = function (e) { - return reject(new ImageKitInvalidRequestError(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR.message)); + return reject(new ImageKitUploadNetworkError(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR.message)); } xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { @@ -204,7 +204,7 @@ export const upload = ( try { var body = JSON.parse(xhr.responseText); return reject(new ImageKitInvalidRequestError( - body.message, + body.message ?? "Invalid request. Please check the parameters.", getResponseMetadata(xhr) )); } catch (ex: any) { @@ -215,11 +215,14 @@ export const upload = ( try { var body = JSON.parse(xhr.responseText); return reject(new ImageKitServerError( - "Server error occurred while uploading the file. This is rare and usually temporary.", + body.message ?? "Server error occurred while uploading the file. This is rare and usually temporary.", getResponseMetadata(xhr) )); } catch (ex: any) { - return reject(ex); + return reject(new ImageKitServerError( + "Server error occurred while uploading the file. This is rare and usually temporary.", + getResponseMetadata(xhr) + )); } } }; diff --git a/test/upload.js b/test/upload.js index ceee2c0..caedb5d 100644 --- a/test/upload.js +++ b/test/upload.js @@ -3,7 +3,13 @@ const sinon = require("sinon"); const expect = chai.expect; import 'regenerator-runtime/runtime'; import { upload } from "../src/index"; -import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError } from '../src/upload'; +import { + ImageKitAbortError, + ImageKitInvalidRequestError, + ImageKitServerError, + ImageKitUploadNetworkError +} from '../src/upload'; + var requests, server; const uploadSuccessResponseObj = { @@ -125,6 +131,7 @@ describe("File upload", async function () { file: "test_file", signature: 'test_signature', expire: 123, + // Omit token publicKey: 'test_public_key' }; @@ -144,7 +151,9 @@ describe("File upload", async function () { fileName: "test_file_name", file: "test_file", token: 'test_token', - expire: 123 + expire: 123, + publicKey: 'test_public_key' + // Omit signature }; const uploadPromise = upload(fileOptions); @@ -153,7 +162,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing signature for upload. The SDK expects token, signature and expire for authentication."); } }); @@ -162,7 +172,9 @@ describe("File upload", async function () { fileName: "test_file_name", file: "test_file", token: 'test_token', - signature: 'test_signature' + signature: 'test_signature', + publicKey: 'test_public_key' + // Omit expire }; const uploadPromise = upload(fileOptions); @@ -171,34 +183,40 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing expire for upload. The SDK expects token, signature and expire for authentication."); } }); it('Missing public key', async function () { const fileOptions = { fileName: "test_file_name", - file: "test_file" + file: "test_file", + token: 'test_token', + signature: 'test_signature', + expire: 123 + // Omit publicKey }; - const imagekitWithoutPublicKey = new ImageKit({ - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", - }); - + const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); try { - const uploadPromise = imagekitWithoutPublicKey.upload(fileOptions); await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ message: "Missing public key for upload", help: "" }); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Missing public key for upload"); } }); it('Upload endpoint network error handling', async function () { const fileOptions = { - ...securityParameters, fileName: "test_file_name", - file: "test_file" + file: "test_file", + token: 'test_token', + signature: 'test_signature', + expire: 123, + publicKey: 'test_public_key' }; const uploadPromise = upload(fileOptions); @@ -211,7 +229,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal({ message: "Request to ImageKit upload endpoint failed due to network error", help: "" }); + expect(ex instanceof ImageKitUploadNetworkError).to.be.true; + expect(ex.message).to.be.equal("Request to ImageKit upload endpoint failed due to network error"); } }); @@ -435,7 +454,7 @@ describe("File upload", async function () { await sleep(); var arg = server.requests[0].requestBody; - + // It's a blob now, check size expect(arg.get('file').size).to.be.eq(buffer.length); expect(arg.get('fileName')).to.be.equal("test_file_name"); expect(arg.get('token')).to.be.equal("test_token"); @@ -466,13 +485,14 @@ describe("File upload", async function () { help: "For support kindly contact us at support@imagekit.io .", message: "Your account cannot be authenticated." }; - errorUploadResponse(500, errRes); + errorUploadResponse(401, errRes); await sleep(); try { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Your account cannot be authenticated."); } }); @@ -499,7 +519,9 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex instanceof SyntaxError).to.be.true; + // The response body is invalid JSON => SyntaxError + expect(ex instanceof ImageKitServerError).to.be.true; + expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); } }); @@ -834,10 +856,10 @@ describe("File upload", async function () { expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); - it('Upload using promise - error', async function () { + it('Server 5xx error with proper json and message', async function () { var errRes = { help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." + message: "Something went wrong" }; const fileOptions = { ...securityParameters, @@ -861,7 +883,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitServerError).to.be.true; + expect(ex.message).to.be.equal("Something went wrong"); } }); @@ -914,8 +937,8 @@ describe("File upload", async function () { }); it('$ResponseMetadata assertions using promise', async function () { - var dummyResonseHeaders = { - "Content-Type": "application/json", + var dummyResponseHeaders = { + "content-type": "application/json", "x-request-id": "sdfsdfsdfdsf" }; const fileOptions = { @@ -936,7 +959,7 @@ describe("File upload", async function () { ] }; - var uploadPromise = upload(fileOptions) + var uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); @@ -944,7 +967,7 @@ describe("File upload", async function () { server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", [ 200, - dummyResonseHeaders, + dummyResponseHeaders, JSON.stringify(uploadSuccessResponseObj) ] ); @@ -952,8 +975,9 @@ describe("File upload", async function () { await sleep(); const response = await uploadPromise; - expect(response.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); - expect(response.$ResponseMetadata.statusCode).to.be.deep.equal(200); + // Make sure your upload.ts preserves the case of "Content-Type" + expect(response.$ResponseMetadata.headers).to.deep.equal(dummyResponseHeaders); + expect(response.$ResponseMetadata.statusCode).to.equal(200); }); it('Undefined fields should not be sent', async function () { @@ -1086,21 +1110,19 @@ describe("File upload", async function () { expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); - it("Should return error for an invalid transformation", async function () { + it("Server 5xx without message", async function () { const fileOptions = { ...securityParameters, fileName: "test_file_name", file: "test_file", responseFields: "tags, customCoordinates, isPrivateFile, metadata", - useUniqueFileName: false, - transformation: {}, + useUniqueFileName: false }; const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); await sleep(); var errRes = { - help: "", - message: "Invalid transformation parameter. Please include at least pre, post, or both.", + help: "" }; errorUploadResponse(500, errRes); await sleep(); @@ -1108,7 +1130,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitServerError).to.be.true; + expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); } }); @@ -1134,7 +1157,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid pre transformation parameter."); } }); @@ -1160,7 +1184,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid post transformation parameter."); } }); @@ -1186,7 +1211,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid post transformation parameter."); } }); @@ -1212,7 +1238,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal(errRes); + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid post transformation parameter."); } }); @@ -1259,7 +1286,7 @@ describe("File upload", async function () { expect(progressSpy.calledOnce).to.be.true; successUploadResponse(); await sleep(); - expect(progressSpy.calledTwice).to.be.true; // for 100% progress + expect(progressSpy.calledTwice).to.be.true; // final progress const response = await uploadPromise; expect(response).to.be.deep.equal(uploadSuccessResponseObj); }); @@ -1280,7 +1307,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex.name).to.be.equal("AbortError"); + expect(ex instanceof ImageKitAbortError).to.be.true; + expect(ex.reason.name).to.be.equal("AbortError"); } }); @@ -1300,7 +1328,8 @@ describe("File upload", async function () { await uploadPromise; throw new Error('Should have thrown error'); } catch (ex) { - expect(ex).to.be.deep.equal("abort reason"); + expect(ex instanceof ImageKitAbortError).to.be.true; + expect(ex.reason).to.be.equal("abort reason"); } }); }); From af7bef3ba723e7e7a6f4e12d5d3f3e56909e02e8 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:07:02 +0530 Subject: [PATCH 111/166] update node js version and build libs --- .babelrc | 5 +- .github/workflows/nodejs.yml | 2 +- .github/workflows/npmpublish.yml | 4 +- .mocharc.json | 4 +- package-lock.json | 13334 ++++++++++++++++------------- package.json | 8 +- rollup.config.js | 2 +- src/upload.ts | 2 - test/setup.js | 2 - test/upload.js | 11 +- 10 files changed, 7173 insertions(+), 6201 deletions(-) diff --git a/.babelrc b/.babelrc index e1f3fc1..081c354 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,8 @@ { - "plugins": ["@babel/plugin-proposal-class-properties"], + "plugins": [ + ["@babel/plugin-transform-class-properties", { "loose": true }], + "@babel/plugin-transform-optional-chaining" +], "presets": [ "@babel/preset-typescript", [ diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index b328240..d12fc3c 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [12.x] + node-version: [20.x] steps: - uses: actions/checkout@v1 diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 0159c40..23298d3 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [12.x] + node-version: [20.x] steps: - uses: actions/checkout@v1 @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: - node-version: 12 + node-version: 20 registry-url: https://registry.npmjs.org/ - name: NPM Publish run: | diff --git a/.mocharc.json b/.mocharc.json index b758d0a..b06f6a6 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -1,6 +1,6 @@ { "coverage": true, - "require": ["esm", "./babel-register.js"], + "require": ["./babel-register.js"], "exit": true, "timeout": "40000" -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index f9475b6..7043aca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6185 +1,7157 @@ { - "name": "imagekit-javascript", - "version": "4.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/cli": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.11.6.tgz", - "integrity": "sha512-+w7BZCvkewSmaRM6H4L2QM3RL90teqEIHDIFXAmrW33+0jhlymnDAEdqVeCZATvxhQuio1ifoGVlJJbIiH9Ffg==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "slash": "^2.0.0", - "source-map": "^0.5.0" - } - }, - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - } - }, - "@babel/core": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", - "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.6", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.5", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", - "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", - "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", - "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz", - "integrity": "sha512-idr3pthFlDCpV+p/rMgGLGYIVtazeatrSOQk8YzO2pAepIjQhCN3myeihVg58ax2bbbGK9PUE1reFi7axOYIOw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.14.4", - "@babel/helper-split-export-declaration": "^7.12.13" - } - }, - "@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", - "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", - "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", - "dev": true - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", - "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", - "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", - "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.4.tgz", - "integrity": "sha512-AYosOWBlyyXEagrPRfLJ1enStufsr7D1+ddpj8OLi9k7B6+NdZ0t/9V7Fh+wJ4g2Jol8z2JkgczYqtWrZd4vbA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.4", - "@babel/helper-compilation-targets": "^7.14.4", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.14.2" - }, - "dependencies": { - "@babel/compat-data": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz", - "integrity": "sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ==", - "dev": true - }, - "@babel/helper-compilation-targets": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz", - "integrity": "sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.4", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - }, - "@babel/plugin-transform-parameters": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", - "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "electron-to-chromium": { - "version": "1.3.743", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.743.tgz", - "integrity": "sha512-K2wXfo9iZQzNJNx67+Pld0DRF+9bYinj62gXCdgPhcu1vidwVuLPHQPPFnCdO55njWigXXpfBiT90jGUPbw8Zg==" - }, - "node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", - "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - } - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", - "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.4.tgz", - "integrity": "sha512-WYdcGNEO7mCCZ2XzRlxwGj3PgeAr50ifkofOUC/+IN/GzKLB+biDPVBUAQN2C/dVZTvEXCp80kfQ1FFZPrwykQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.14.4", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-typescript": "^7.12.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz", - "integrity": "sha512-idr3pthFlDCpV+p/rMgGLGYIVtazeatrSOQk8YzO2pAepIjQhCN3myeihVg58ax2bbbGK9PUE1reFi7axOYIOw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.14.4", - "@babel/helper-split-export-declaration": "^7.12.13" - } - }, - "@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", - "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", - "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", - "dev": true - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", - "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz", - "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.5", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", - "dev": true, - "requires": { - "@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" - } - }, - "@babel/preset-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", - "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-typescript": "^7.13.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true - } - } - }, - "@babel/register": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel%2fregister/-/register-7.14.5.tgz", - "integrity": "sha512-TjJpGz/aDjFGWsItRBQMOFTrmTI9tr79CHOK+KIvLeCkbxuOAk2M5QHjvruIMGoo9OuccMh5euplPzc5FjAKGg==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" - }, - "dependencies": { - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@rollup/plugin-babel": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz", - "integrity": "sha512-Jd7oqFR2dzZJ3NWANDyBjwTtX/lYbZpVcmkHrfQcpvawHs9E4c0nYk5U2mfZ6I/DZcIvy506KZJi54XK/jxH7A==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - } - }, - "@rollup/plugin-json": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", - "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8" - } - }, - "@rollup/plugin-node-resolve": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", - "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deep-freeze": "^0.0.1", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.17.0" - } - }, - "@rollup/plugin-typescript": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz", - "integrity": "sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "resolve": "^1.17.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - } - }, - "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-4.0.1.tgz", - "integrity": "sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^4.2.0" - } - }, - "@sinonjs/samsam": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-4.2.2.tgz", - "integrity": "sha512-z9o4LZUzSD9Hl22zV38aXNykgFeVj8acqfFabCY6FY83n/6s/XwNJyYYldz6/9lBJanpno9h+oL6HTISkviweA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz", - "integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz", - "integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz", - "integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz", - "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==", - "dev": true - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/node": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.1.tgz", - "integrity": "sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==", - "dev": true - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "abortcontroller-polyfill": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.8.tgz", - "integrity": "sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ==", - "dev": true - }, - "agent-base": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", - "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", - "dev": true, - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", - "dev": true - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "optional": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "optional": true - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "optional": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "optional": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "dependencies": { - "electron-to-chromium": { - "version": "1.3.776", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.776.tgz", - "integrity": "sha512-V0w7eFSBoFPpdw4xexjVPZ770UDZIevSwkkj4W97XbE3IsCsTRFpa7/yXGZ88EOQAUEA09JMMsWK0xsw0kRAYw==", - "dev": true - }, - "node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - } - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "optional": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001232", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001232.tgz", - "integrity": "sha512-e4Gyp7P8vqC2qV2iHA+cJNf/yqUKOShXQOJHQt81OHxlIZl/j/j3soEA0adAQi8CPUQgvOdDENyQ5kd6a6mNSg==", - "dev": true - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "codecov": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.0.tgz", - "integrity": "sha512-7E/S7hmq2CJvCMBMu+aRACO9jxQX1HJug/M3ub8+t84R+5Ai2T5sFMxS3W8P41m2A63+VSAAL4U0aBlqZXkJPw==", - "dev": true, - "requires": { - "argv": "0.0.2", - "ignore-walk": "3.0.3", - "js-yaml": "3.14.0", - "teeny-request": "7.0.1", - "urlgrey": "0.4.4" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "optional": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "optional": true - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "dev": true - }, - "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", - "dev": true, - "requires": { - "browserslist": "^4.8.5", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "optional": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-freeze": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", - "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "optional": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "formdata-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-2.1.0.tgz", - "integrity": "sha512-CnnnN2OICKs+qyybHU2bAobkj8OSeRJD/mSqkzbMHtTVDF0WMhbi/VtuKCQF4LHCqSKqENFzrs3rwKNW2dm2lQ==", - "dev": true, - "requires": { - "@babel/runtime": "7.7.7", - "mime-types": "2.1.25", - "nanoid": "2.1.8" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", - "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.2" - } - } - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "optional": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fromentries": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.1.tgz", - "integrity": "sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw==", - "dev": true - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "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-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true, - "optional": true - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "optional": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest-worker": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", - "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-cleanup": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", - "integrity": "sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==", - "dev": true, - "requires": { - "magic-string": "^0.25.7", - "perf-regexes": "^1.0.1", - "skip-regex": "^1.0.2" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "just-extend": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", - "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "optional": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "optional": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", - "dev": true, - "requires": { - "mime-db": "1.42.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "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-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true, - "optional": true - }, - "nanoid": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.8.tgz", - "integrity": "sha512-g1z+n5s26w0TGKh7gjn7HCqurNKMZWzH08elXzh/gM/csQHd/UqDV6uxMghQYg9IvqRPm1QpeMk50YMofHvEjQ==", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "nise": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-3.0.1.tgz", - "integrity": "sha512-fYcH9y0drBGSoi88kvhpbZEsenX58Yr+wOJ4/Mi1K4cy+iGP/a73gNoyNhu5E9QxPdgTlVChfIaAlnyOy/gHUA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/formatio": "^4.0.1", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^5.0.1", - "path-to-regexp": "^1.7.0" - } - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "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": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "optional": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "optional": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "perf-regexes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", - "integrity": "sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "optional": true - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true, - "optional": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "optional": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "optional": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true, - "optional": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "optional": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.29.0.tgz", - "integrity": "sha512-gtU0sjxMpsVlpuAf4QXienPmUAhd6Kc7owQ4f5lypoxBW18fw2UNYZ4NssLGsri6WhUZkE/Ts3EMRebN+gNLiQ==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - }, - "dependencies": { - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - } - } - }, - "rollup-plugin-cleanup": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz", - "integrity": "sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==", - "dev": true, - "requires": { - "js-cleanup": "^1.2.0", - "rollup-pluginutils": "^2.8.2" - } - }, - "rollup-plugin-commonjs": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", - "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", - "dev": true, - "requires": { - "estree-walker": "^0.6.1", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0", - "rollup-pluginutils": "^2.8.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - } - } - }, - "rollup-plugin-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", - "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "jest-worker": "^26.0.0", - "serialize-javascript": "^3.0.0", - "terser": "^4.7.0" - } - }, - "rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "requires": { - "estree-walker": "^0.6.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "optional": true, - "requires": { - "ret": "~0.1.10" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sinon": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.1.1.tgz", - "integrity": "sha512-E+tWr3acRdoe1nXbHMu86SSqA1WGM7Yw3jZRLvlCMnXwTHP8lgFFVn5BnKnF26uc5SfZ3D7pA9sN7S3Y2jG4Ew==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/formatio": "^4.0.1", - "@sinonjs/samsam": "^4.2.2", - "diff": "^4.0.2", - "lolex": "^5.1.2", - "nise": "^3.0.1", - "supports-color": "^7.1.0" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "skip-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", - "integrity": "sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "optional": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "optional": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true, - "optional": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "optional": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stream-events": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "dev": true, - "requires": { - "stubs": "^3.0.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "teeny-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", - "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", - "dev": true, - "requires": { - "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" - } - }, - "terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "ts-node": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz", - "integrity": "sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==", - "dev": true, - "requires": { - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "optional": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "optional": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true, - "optional": true - }, - "urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true - }, - "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true - }, - "web-blob": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/web-blob/-/web-blob-2.1.3.tgz", - "integrity": "sha512-7ARypAQxyyqT7ZCHydKavBGCzvvhicGuUMFqg6Xua6H0HIqGgEH1VsMVr1P1Nx+D/maqrUNyOzHkTjBQH/bZOQ==", - "dev": true, - "requires": { - "web-streams-polyfill": "2.1.1" - } - }, - "web-file-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/web-file-polyfill/-/web-file-polyfill-1.0.1.tgz", - "integrity": "sha512-Avo1SzN2bpF9w1bmw0Rz455VKjjMgkA32GnDTCvMTNyKs3/7xq3J+yhR1820rqbkIU3LZfx7mU4PTnZXOqQ6uA==", - "dev": true, - "requires": { - "web-blob": "2.1.3" - } - }, - "web-streams-polyfill": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-2.1.1.tgz", - "integrity": "sha512-dlNpL2aab3g8CKfGz6rl8FNmGaRWLLn2g/DtSc9IjB30mEdE6XxzPfPSig5BwGSzI+oLxHyETrQGKjrVVhbLCg==", - "dev": true - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.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": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } + "name": "imagekit-javascript", + "version": "4.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "imagekit-javascript", + "version": "4.0.1", + "license": "MIT", + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.10.5", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/preset-env": "^7.10.4", + "@babel/preset-typescript": "^7.13.0", + "@babel/register": "^7.14.5", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^8.4.0", + "@rollup/plugin-typescript": "^8.2.1", + "@types/node": "^15.6.1", + "babel-plugin-transform-class-properties": "^6.24.1", + "chai": "^4.2.0", + "codecov": "^3.8.0", + "formdata-node": "2.1.0", + "mocha": "^7.0.1", + "nyc": "^15.1.0", + "regenerator-runtime": "^0.13.9", + "rollup": "^2.22.0", + "rollup-plugin-cleanup": "^3.2.1", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-terser": "^6.1.0", + "sinon": "^8.1.1", + "ts-node": "^10.0.0", + "typescript": "^4.3.2", + "web-file-polyfill": "^1.0.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/cli": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.27.0.tgz", + "integrity": "sha512-bZfxn8DRxwiVzDO5CEeV+7IqXeCkzI4yYnrQbpwjT76CUyossQc6RYE7n+xfm0/2k40lPaCpW0FhxYs7EBAetw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "commander": "^6.2.0", + "convert-source-map": "^2.0.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.6.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz", + "integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.27.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz", + "integrity": "sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.26.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz", + "integrity": "sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz", + "integrity": "sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz", + "integrity": "sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.0.tgz", + "integrity": "sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.27.0", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.40.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.0.tgz", + "integrity": "sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-typescript": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.9.tgz", + "integrity": "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.6", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", + "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.2" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.8" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", + "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deep-freeze": "^0.0.1", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz", + "integrity": "sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/formatio": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-4.0.1.tgz", + "integrity": "sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^4.2.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-4.2.2.tgz", + "integrity": "sha512-z9o4LZUzSD9Hl22zV38aXNykgFeVj8acqfFabCY6FY83n/6s/XwNJyYYldz6/9lBJanpno9h+oL6HTISkviweA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "15.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argv": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", + "integrity": "sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "engines": { + "node": ">=0.6.10" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz", + "integrity": "sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "is-string": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true + }, + "node_modules/babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==", + "dev": true, + "dependencies": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", + "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", + "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha512-chI3Rt9T1AbrQD1s+vxw3KcwC9yHtF621/MacuItITfZX344uhQoANjpoSJZleAmW2tjlolqB/f+h7jIqXa7pA==", + "dev": true + }, + "node_modules/babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha512-n4jtBA3OYBdvG5PRMKsMXJXHfLYw/ZOmtxCLOOwz6Ro5XlrColkStLnz1AS1L2yfPA9BKJ1ZNlmVCLjAL9DSIg==", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dev": true, + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "node_modules/babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/babel-traverse/node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-traverse/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001711", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001711.tgz", + "integrity": "sha512-OpFA8GsKtoV3lCcsI3U5XBAV+oVrMu96OS8XafKqnhOaEAW2mveD1Mx81Sx/02chERwhDakuXs28zbyEc4QMKg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "optional": true, + "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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/codecov": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz", + "integrity": "sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==", + "deprecated": "https://about.codecov.io/blog/codecov-uploader-deprecation-plan/", + "dev": true, + "dependencies": { + "argv": "0.0.2", + "ignore-walk": "3.0.4", + "js-yaml": "3.14.1", + "teeny-request": "7.1.1", + "urlgrey": "1.0.0" + }, + "bin": { + "codecov": "bin/codecov" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/core-js-compat": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "dev": true, + "dependencies": { + "browserslist": "^4.24.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-freeze": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", + "integrity": "sha512-Z+z8HiAvsGwmjqlphnHW5oz6yWlOwu6EQfFTjmeTWlDeda3FS2yv3jhq35TX/ewmsnqB+RX2IdsIOyjJCQN5tg==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", + "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract/node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "dev": true, + "dependencies": { + "punycode": "^1.3.2" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/formdata-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-2.1.0.tgz", + "integrity": "sha512-CnnnN2OICKs+qyybHU2bAobkj8OSeRJD/mSqkzbMHtTVDF0WMhbi/VtuKCQF4LHCqSKqENFzrs3rwKNW2dm2lQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "7.7.7", + "mime-types": "2.1.25", + "nanoid": "2.1.8" + }, + "engines": { + "node": ">= 10.2.x" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-cleanup": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", + "integrity": "sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.25.7", + "perf-regexes": "^1.0.1", + "skip-regex": "^1.0.2" + }, + "engines": { + "node": "^10.14.2 || >=12.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true + }, + "node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "dev": true, + "dependencies": { + "mime-db": "1.42.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", + "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "dev": true, + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "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.2.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.1" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/mocha/node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.8.tgz", + "integrity": "sha512-g1z+n5s26w0TGKh7gjn7HCqurNKMZWzH08elXzh/gM/csQHd/UqDV6uxMghQYg9IvqRPm1QpeMk50YMofHvEjQ==", + "dev": true + }, + "node_modules/nise": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-3.0.1.tgz", + "integrity": "sha512-fYcH9y0drBGSoi88kvhpbZEsenX58Yr+wOJ4/Mi1K4cy+iGP/a73gNoyNhu5E9QxPdgTlVChfIaAlnyOy/gHUA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/formatio": "^4.0.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^5.0.1", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "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" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nyc/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/nyc/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/nyc/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "dev": true, + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/perf-regexes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", + "integrity": "sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==", + "dev": true, + "engines": { + "node": ">=6.14" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regenerator-transform/node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/regenerator-transform/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-cleanup": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz", + "integrity": "sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==", + "dev": true, + "dependencies": { + "js-cleanup": "^1.2.0", + "rollup-pluginutils": "^2.8.2" + }, + "engines": { + "node": "^10.14.2 || >=12.0.0" + }, + "peerDependencies": { + "rollup": ">=2.0" + } + }, + "node_modules/rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-commonjs.", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "rollup": ">=1.12.0" + } + }, + "node_modules/rollup-plugin-commonjs/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/rollup-plugin-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", + "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "jest-worker": "^26.0.0", + "serialize-javascript": "^3.0.0", + "terser": "^4.7.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sinon": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.1.1.tgz", + "integrity": "sha512-E+tWr3acRdoe1nXbHMu86SSqA1WGM7Yw3jZRLvlCMnXwTHP8lgFFVn5BnKnF26uc5SfZ3D7pA9sN7S3Y2jG4Ew==", + "deprecated": "16.1.1", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/formatio": "^4.0.1", + "@sinonjs/samsam": "^4.2.2", + "diff": "^4.0.2", + "lolex": "^5.1.2", + "nise": "^3.0.1", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/skip-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", + "integrity": "sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==", + "dev": true, + "engines": { + "node": ">=4.2" + } + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spawn-wrap/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true + }, + "node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz", + "integrity": "sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/urlgrey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz", + "integrity": "sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w==", + "dev": true, + "dependencies": { + "fast-url-parser": "^1.1.3" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/web-blob": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/web-blob/-/web-blob-2.1.3.tgz", + "integrity": "sha512-7ARypAQxyyqT7ZCHydKavBGCzvvhicGuUMFqg6Xua6H0HIqGgEH1VsMVr1P1Nx+D/maqrUNyOzHkTjBQH/bZOQ==", + "dev": true, + "dependencies": { + "web-streams-polyfill": "2.1.1" + } + }, + "node_modules/web-file-polyfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/web-file-polyfill/-/web-file-polyfill-1.0.4.tgz", + "integrity": "sha512-AnwI+/bksR0DCY2zobfqDSMi529yzWCxUIuMc8jbC5qJNWGOnBgiWY0+KD5soiw40+IsNXi0Zl5W/dm12438NA==", + "dev": true, + "dependencies": { + "web-blob": "2.1.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-2.1.1.tgz", + "integrity": "sha512-dlNpL2aab3g8CKfGz6rl8FNmGaRWLLn2g/DtSc9IjB30mEdE6XxzPfPSig5BwGSzI+oLxHyETrQGKjrVVhbLCg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.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": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true } - } } diff --git a/package.json b/package.json index 195bdb0..9658810 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,9 @@ "devDependencies": { "@babel/cli": "^7.10.5", "@babel/core": "^7.10.5", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", + "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/preset-env": "^7.10.4", "@babel/preset-typescript": "^7.13.0", "@babel/register": "^7.14.5", @@ -22,11 +25,9 @@ "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-typescript": "^8.2.1", "@types/node": "^15.6.1", - "abortcontroller-polyfill": "^1.7.8", "babel-plugin-transform-class-properties": "^6.24.1", "chai": "^4.2.0", "codecov": "^3.8.0", - "esm": "^3.2.25", "formdata-node": "2.1.0", "mocha": "^7.0.1", "nyc": "^15.1.0", @@ -70,6 +71,5 @@ "bugs": { "url": "https://github.com/imagekit-developer/imagekit-javascript/issues" }, - "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme", - "dependencies": {} + "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme" } diff --git a/rollup.config.js b/rollup.config.js index 95ae3de..aaf93db 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,7 +20,7 @@ export default [ babel({ extensions: [".ts"], }), - terser(), + // terser(), cleanup(), ], }, diff --git a/src/upload.ts b/src/upload.ts index eb578ef..dbbffde 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -159,7 +159,6 @@ export const upload = ( xhr.abort(); return reject(new ImageKitAbortError( "Upload aborted", - // @ts-ignore for TypeScript versions lacking `signal.reason` uploadOptions.signal?.reason )); } @@ -170,7 +169,6 @@ export const upload = ( return reject(new ImageKitAbortError( "Upload aborted", - // @ts-ignore for TypeScript versions lacking `signal.reason` uploadOptions.signal?.reason )); } diff --git a/test/setup.js b/test/setup.js index 51de158..5decfdd 100644 --- a/test/setup.js +++ b/test/setup.js @@ -2,8 +2,6 @@ global.FormData = require("formdata-node"); global.Blob = require("web-file-polyfill").Blob global.File = require("web-file-polyfill").File -const { AbortController, abortableFetch } = require('abortcontroller-polyfill/dist/cjs-ponyfill'); -global.AbortController = AbortController global.ProgressEvent = class FakeProgressEvent { constructor(type, init = {}) { this.type = type; diff --git a/test/upload.js b/test/upload.js index caedb5d..ada51f4 100644 --- a/test/upload.js +++ b/test/upload.js @@ -63,6 +63,7 @@ function errorUploadResponse(statusCode, obj) { } async function sleep(ms = 0) { + return true; return new Promise((resolve) => { setTimeout(resolve, ms); }); @@ -220,11 +221,11 @@ describe("File upload", async function () { }; const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - // Simulate network error on upload API - server.requests[0].error(); - await sleep(); + expect(server.requests.length).to.be.equal(1); + await sleep(); + // Simulate network error on upload API + server.requests[0].error(); + await sleep(); try { await uploadPromise; throw new Error('Should have thrown error'); From 9ad887e8d2190f04f568f1610a388d61c38e21a6 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:19:41 +0530 Subject: [PATCH 112/166] fix: enable terser for code minification in rollup configuration --- rollup.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup.config.js b/rollup.config.js index aaf93db..95ae3de 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -20,7 +20,7 @@ export default [ babel({ extensions: [".ts"], }), - // terser(), + terser(), cleanup(), ], }, From c2edc0b269cd1f58c124ddccb6cab67f892c0633 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:22:05 +0530 Subject: [PATCH 113/166] fix: update codecov version to 3.8.3 and modify coverage reporting script --- package-lock.json | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7043aca..8d1556b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/node": "^15.6.1", "babel-plugin-transform-class-properties": "^6.24.1", "chai": "^4.2.0", - "codecov": "^3.8.0", + "codecov": "^3.8.3", "formdata-node": "2.1.0", "mocha": "^7.0.1", "nyc": "^15.1.0", diff --git a/package.json b/package.json index 9658810..17faa3f 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/node": "^15.6.1", "babel-plugin-transform-class-properties": "^6.24.1", "chai": "^4.2.0", - "codecov": "^3.8.0", + "codecov": "^3.8.3", "formdata-node": "2.1.0", "mocha": "^7.0.1", "nyc": "^15.1.0", @@ -47,7 +47,7 @@ "build": "rm -rf dist*;rollup -c && yarn export-types", "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", - "report-coverage": "codecov" + "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "repository": { "type": "git", From cb7c1351025ea7c8e3465404448bbe7c9076af2d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:25:29 +0530 Subject: [PATCH 114/166] ci: add Codecov upload step to GitHub Actions workflow --- .github/workflows/nodejs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index d12fc3c..102ba51 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -26,6 +26,7 @@ jobs: npm install npm run build npm run test - npm run report-coverage env: CI: true + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 From b40208d0748d403bff925c94cb4097a7c1e87142 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:32:00 +0530 Subject: [PATCH 115/166] fix: remove coverage reporting step from startSampleApp script --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 17faa3f..593d01c 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,7 @@ "export-types": "tsc", "build": "rm -rf dist*;rollup -c && yarn export-types", "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", - "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", - "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" + "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" }, "repository": { "type": "git", From 990b74d79f07182a888f1d84ffd7f60b74b204df Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 16:54:56 +0530 Subject: [PATCH 116/166] test: add upload error handling tests for abort signal and invalid responses --- test/upload.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/test/upload.js b/test/upload.js index ada51f4..a2b8950 100644 --- a/test/upload.js +++ b/test/upload.js @@ -221,11 +221,11 @@ describe("File upload", async function () { }; const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - // Simulate network error on upload API - server.requests[0].error(); - await sleep(); + expect(server.requests.length).to.be.equal(1); + await sleep(); + // Simulate network error on upload API + server.requests[0].error(); + await sleep(); try { await uploadPromise; throw new Error('Should have thrown error'); @@ -1333,4 +1333,63 @@ describe("File upload", async function () { expect(ex.reason).to.be.equal("abort reason"); } }); + + it("Already aborted signal should abort upload immediately", async function () { + const abortController = new AbortController(); + // Abort the signal before calling upload + abortController.abort(); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + signal: abortController.signal + }; + try { + await upload(fileOptions); + throw new Error("Should have thrown error"); + } catch (ex) { + expect(ex instanceof ImageKitAbortError).to.be.true; + expect(ex.reason && ex.reason.name).to.be.equal("AbortError"); + } + }); + + it("Error during upload 4xx with invalid JSON response", async function () { + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file" + }; + const uploadPromise = upload(fileOptions); + // errorUploadResponse(400, `{sd`); + server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", + [ + 400, + { "Content-Type": "application/json" }, + "sdf" + ] + ); + server.respond(); + try { + await uploadPromise; + throw new Error("Should have thrown error"); + } catch (ex) { + expect(ex).to.be.instanceOf(SyntaxError); + } + }); + + it("Should return error for an invalid transformation object in upload", async function () { + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: "test_file", + transformation: 123 + }; + try { + await upload(fileOptions); + throw new Error("Should have thrown error"); + } catch (ex) { + expect(ex instanceof ImageKitInvalidRequestError).to.be.true; + expect(ex.message).to.be.equal("Invalid transformation parameter. Please include at least pre, post, or both."); + } + }); }); From 4d30974ed612a9b985c4b71d297cc96013577222 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sat, 5 Apr 2025 17:03:02 +0530 Subject: [PATCH 117/166] improve test coverage --- src/url.ts | 11 ++++------- src/utils/transformation.ts | 8 +------- test/url-generation/basic.js | 12 +++++++++++- test/url-generation/overlay.js | 13 +++++++++++++ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/url.ts b/src/url.ts index a52c44c..228b272 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,7 +1,6 @@ import { UrlOptions } from "./interfaces"; import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; -import transformationUtils from "./utils/transformation"; -import { safeBtoa } from "./utils/transformation"; +import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. @@ -118,13 +117,11 @@ function processText(str: string, enccoding: TextOverlay["encoding"]): string { function processOverlay(overlay: Transformation["overlay"]): string | undefined { const entries = []; - if (!overlay) { - return; - } - const { type, position = {}, timing = {}, transformation = [] } = overlay; + + const { type, position = {}, timing = {}, transformation = [] } = overlay || {}; if (!type) { - throw new Error("Overlay type is required"); + return; } switch (type) { diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index 30870a1..9a3a981 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -10,16 +10,9 @@ const TRANSFORM_DELIMITER: string = ","; const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; export default { - getDefault: (): TransformationPosition => { - return DEFAULT_TRANSFORMATION_POSITION; - }, addAsQueryParameter: (options: UrlOptions) => { return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; }, - validParameters: (options: UrlOptions) => { - if (typeof options.transformationPosition == "undefined") return false; - return VALID_TRANSFORMATION_POSITIONS.indexOf(options.transformationPosition) != -1; - }, getTransformKey: function (transform: string) { if (!transform) { return ""; } @@ -38,6 +31,7 @@ export default { export const safeBtoa = function (str: string): string { if (typeof window !== "undefined") { + /* istanbul ignore next */ return btoa(str); } else { // Node fallback diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index aa3200d..86376ed 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -12,7 +12,7 @@ describe("URL generation", function () { expect(url).equal(""); }); - it('should return an empty string for an invalid src URL', function () { + it('should return an empty string when src is /', function () { const url = buildURL({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", @@ -22,6 +22,16 @@ describe("URL generation", function () { expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); }); + it('should return an empty string when src is invalid', function () { + const url = buildURL({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + transformationPosition: "query", + src: "https://" + }); + + expect(url).equal(""); + }); + it('should generate a valid URL when src is provided without transformation', function () { const url = buildURL({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 880f9c8..fccdbdc 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -19,6 +19,19 @@ describe("Overlay Transformation Test Cases", function () { expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); }); + it('Ignore if type is missing', function () { + const url = buildURL({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "/base-image.jpg", + transformation: [{ + overlay: { + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + it('Ignore invalid values if input (image)', function () { const url = buildURL({ transformationPosition: "path", From bbe982b208f810ba93978a326ae83f6f5a4f0a67 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 14:06:29 +0530 Subject: [PATCH 118/166] chore: update package version to 5.0.0.beta.1 and add .npmignore --- .gitignore | 3 ++- .npmignore | 1 + package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .npmignore diff --git a/.gitignore b/.gitignore index 48d5826..9361b82 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist .nyc_output coverage.lcov coverage -out-tsc \ No newline at end of file +out-tsc +docs \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..483a9c4 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +package-lock.json \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8d1556b..50d96c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "imagekit-javascript", - "version": "4.0.1", + "version": "5.0.0.beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "imagekit-javascript", - "version": "4.0.1", + "version": "5.0.0.beta.1", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 593d01c..0136fb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imagekit-javascript", - "version": "4.0.1", + "version": "5.0.0.beta.1", "description": "Javascript SDK for using ImageKit.io in the browser", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From ed16142d3aefc2e56963e250146fac86d6b06dc3 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 14:52:21 +0530 Subject: [PATCH 119/166] refactor: remove IKResponse interface and update related imports; introduce SrcOptions interface for upload functionality --- src/index.ts | 14 +- src/interfaces/IKResponse.ts | 10 -- .../{UrlOptions.ts => SrcOptions.ts} | 2 +- src/interfaces/UploadOptions.ts | 4 +- src/interfaces/UploadResponse.ts | 4 +- src/interfaces/index.ts | 8 +- src/upload.ts | 14 +- src/url.ts | 10 +- src/utils/transformation.ts | 4 +- test/upload.js | 5 +- test/url-generation/basic.js | 160 +++++++++--------- test/url-generation/overlay.js | 52 +++--- 12 files changed, 137 insertions(+), 150 deletions(-) delete mode 100644 src/interfaces/IKResponse.ts rename src/interfaces/{UrlOptions.ts => SrcOptions.ts} (97%) diff --git a/src/index.ts b/src/index.ts index c43d292..95ab767 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,13 @@ -import { UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; -import { upload } from "./upload"; -import { buildURL, generateTransformationString } from "./url"; - -export { buildURL, generateTransformationString, upload }; +import { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; +import { buildSrc, buildTransformationString } from "./url"; +export { buildSrc, buildTransformationString, upload, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { - UrlOptions, + Transformation, + SrcOptions, UploadOptions, UploadResponse }; + + diff --git a/src/interfaces/IKResponse.ts b/src/interfaces/IKResponse.ts deleted file mode 100644 index a53ca4f..0000000 --- a/src/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/src/interfaces/UrlOptions.ts b/src/interfaces/SrcOptions.ts similarity index 97% rename from src/interfaces/UrlOptions.ts rename to src/interfaces/SrcOptions.ts index ff88bed..bf8f914 100644 --- a/src/interfaces/UrlOptions.ts +++ b/src/interfaces/SrcOptions.ts @@ -1,7 +1,7 @@ import { Transformation } from "./Transformation"; import { TransformationPosition } from "."; -export interface UrlOptions { +export interface SrcOptions { /** * Accepts relative or absolute path of the resource. If relative path is provided, it is appended to the `urlEndpoint`. If absolute path is provided, `urlEndpoint` is ignored. */ diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 3b84411..cd22f9d 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -21,9 +21,7 @@ interface Transformation { post?: PostTransformation[] } /** - * Options used when uploading a file - * - * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Request} + * Options used when uploading a file. Checkout [upload docs](https://imagekit.io/docs/api-reference/upload-file/upload-file#Request) for more details. */ export interface UploadOptions { /** diff --git a/src/interfaces/UploadResponse.ts b/src/interfaces/UploadResponse.ts index 963d2c0..dbae9c0 100644 --- a/src/interfaces/UploadResponse.ts +++ b/src/interfaces/UploadResponse.ts @@ -6,7 +6,7 @@ * * {@link https://imagekit.io/docs/api-reference/digital-asset-management-dam/list-and-search-assets} */ -export type FileType = "all" | "image" | "non-image"; +type FileType = "all" | "image" | "non-image"; /** * Metadata object structure @@ -23,7 +23,7 @@ export type FileType = "all" | "image" | "non-image"; * * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on the image's contents. It is different from cryptographic hash functions like MD5 and SHA1. pHash provides similar hash value after minor distortions, like small rotations, blurring, and compression in the image. */ -export interface Metadata { +interface Metadata { height: number; width: number; size: number; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 71a6ec6..9e2f4c7 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,7 +1,7 @@ -import { TransformationPosition } from "./Transformation"; +import { Transformation, TransformationPosition } from "./Transformation"; import { UploadOptions } from "./UploadOptions"; -import { FileType, UploadResponse } from "./UploadResponse"; -import { UrlOptions } from "./UrlOptions"; +import { ResponseMetadata, UploadResponse } from "./UploadResponse"; +import { SrcOptions } from "./SrcOptions"; -export type { FileType, TransformationPosition, UploadOptions, UploadResponse, UrlOptions }; +export type { ResponseMetadata, Transformation, TransformationPosition, UploadOptions, UploadResponse, SrcOptions }; diff --git a/src/upload.ts b/src/upload.ts index dbbffde..a601094 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,7 +1,5 @@ import errorMessages from "./constants/errorMessages"; -import { UploadOptions } from "./interfaces/UploadOptions"; -import { ResponseMetadata, UploadResponse } from "./interfaces/UploadResponse"; - +import { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; export class ImageKitInvalidRequestError extends Error { /** @@ -47,12 +45,12 @@ export class ImageKitServerError extends Error { } /** - * Uploads a file with the given upload options. + * Uploads a file to ImageKit with the given upload options. * - * @throws {ImageKitInvalidRequestError} If the request is invalid. - * @throws {ImageKitAbortError} If the request is aborted. - * @throws {ImageKitUploadNetworkError} If there is a network error. - * @throws {ImageKitServerError} If there is a server error. + * @throws {@link ImageKitInvalidRequestError} If the request is invalid. + * @throws {@link ImageKitAbortError} If the request is aborted. + * @throws {@link ImageKitUploadNetworkError} If there is a network error. + * @throws {@link ImageKitServerError} If there is a server error. * * @param {UploadOptions} uploadOptions - The options for uploading the file. * @returns A Promise resolving to a successful {@link UploadResponse} diff --git a/src/url.ts b/src/url.ts index 228b272..8e98e95 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,4 +1,4 @@ -import { UrlOptions } from "./interfaces"; +import { SrcOptions } from "./interfaces"; import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; @@ -25,7 +25,7 @@ function pathJoin(parts: string[], sep?: string) { return parts.join(separator).replace(replace, separator); } -export const buildURL = (opts: UrlOptions) => { +export const buildSrc = (opts: SrcOptions) => { opts.urlEndpoint = opts.urlEndpoint || ""; opts.src = opts.src || ""; opts.transformationPosition = opts.transformationPosition || "query"; @@ -55,7 +55,7 @@ export const buildURL = (opts: UrlOptions) => { urlObj.searchParams.append(i, String(opts.queryParameters[i])); } - var transformationString = generateTransformationString(opts.transformation); + var transformationString = buildTransformationString(opts.transformation); if (transformationString && transformationString.length) { if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { @@ -209,7 +209,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined entries.push(`ldu-${duration}`); } - const transformationString = generateTransformationString(transformation); + const transformationString = buildTransformationString(transformation); if (transformationString && transformationString.trim() !== "") entries.push(transformationString); @@ -218,7 +218,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined return entries.join(transformationUtils.getTransformDelimiter()); } -export const generateTransformationString = function (transformation: Transformation[] | undefined) { +export const buildTransformationString = function (transformation: Transformation[] | undefined) { if (!Array.isArray(transformation)) { return ""; } diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index 9a3a981..9578f74 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -1,5 +1,5 @@ import supportedTransforms from "../constants/supportedTransforms"; -import { TransformationPosition, UrlOptions } from "../interfaces"; +import { TransformationPosition, SrcOptions } from "../interfaces"; const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; const PATH_TRANSFORMATION_POSITION: TransformationPosition = "path"; @@ -10,7 +10,7 @@ const TRANSFORM_DELIMITER: string = ","; const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; export default { - addAsQueryParameter: (options: UrlOptions) => { + addAsQueryParameter: (options: SrcOptions) => { return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; }, getTransformKey: function (transform: string) { diff --git a/test/upload.js b/test/upload.js index a2b8950..b198dda 100644 --- a/test/upload.js +++ b/test/upload.js @@ -2,13 +2,12 @@ const chai = require("chai"); const sinon = require("sinon"); const expect = chai.expect; import 'regenerator-runtime/runtime'; -import { upload } from "../src/index"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, - ImageKitUploadNetworkError -} from '../src/upload'; + ImageKitUploadNetworkError, upload +} from "../src/index"; var requests, server; diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index 86376ed..739958c 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -1,10 +1,10 @@ const chai = require("chai"); const expect = chai.expect; -import { buildURL } from "../../src/index"; +import { buildSrc } from "../../src/index"; describe("URL generation", function () { it('should return an empty string when src is not provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query" }); @@ -13,7 +13,7 @@ describe("URL generation", function () { }); it('should return an empty string when src is /', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/" @@ -23,7 +23,7 @@ describe("URL generation", function () { }); it('should return an empty string when src is invalid', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "https://" @@ -33,7 +33,7 @@ describe("URL generation", function () { }); it('should generate a valid URL when src is provided without transformation', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg" @@ -43,7 +43,7 @@ describe("URL generation", function () { }); it('should generate a valid URL when a src is provided without transformation', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" @@ -53,7 +53,7 @@ describe("URL generation", function () { }); it('should generate a valid URL when undefined transformation parameters are provided with path', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path_alt.jpg", transformation: undefined, @@ -64,7 +64,7 @@ describe("URL generation", function () { }); it("By default transformationPosition should be query", function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path.jpg", transformation: [ @@ -81,7 +81,7 @@ describe("URL generation", function () { }); it('should generate the URL without sdk version', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path.jpg", transformation: [ @@ -97,7 +97,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with a valid src and transformation', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -114,7 +114,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when the provided path contains multiple leading slashes', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "///test_path.jpg", @@ -130,7 +130,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when the urlEndpoint is overridden', function () { - const url = buildURL({ + const url = buildSrc({ // We do not override urlEndpoint here urlEndpoint: "https://ik.imagekit.io/test_url_endpoint_alt", transformationPosition: "query", @@ -147,7 +147,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/test_path.jpg", transformationPosition: "query", @@ -163,7 +163,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with a valid src parameter and transformation', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", @@ -179,7 +179,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", transformationPosition: "query", @@ -195,7 +195,7 @@ describe("URL generation", function () { }); it('should merge query parameters correctly in the generated URL', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", @@ -212,7 +212,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with chained transformations', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -231,7 +231,7 @@ describe("URL generation", function () { }); it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -250,7 +250,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when overlay image transformation is provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -267,7 +267,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -284,7 +284,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when border transformation is applied', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -301,7 +301,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when transformation has empty key and value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -316,7 +316,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when an undefined transform is provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -331,7 +331,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when transformation key has an empty value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -346,7 +346,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when transformation key has \'-\' as its value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -361,7 +361,7 @@ describe("URL generation", function () { }); it('should skip transformation parameters that are undefined or null', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -378,7 +378,7 @@ describe("URL generation", function () { }); it('should skip transformation parameters that are false', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -394,7 +394,7 @@ describe("URL generation", function () { }); it('should include only the key when transformation value is an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -410,7 +410,7 @@ describe("URL generation", function () { }); it('should include both key and value when transformation parameter value is provided', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -426,7 +426,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when trim transformation is set to true as a boolean', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -442,7 +442,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when trim transformation is set to true as a string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -458,7 +458,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for AI background removal when set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -473,7 +473,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for AI background removal when \'true\' is provided as a string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -488,7 +488,7 @@ describe("URL generation", function () { }); it('should not apply AI background removal when value is not true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -503,7 +503,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for external AI background removal when set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -518,7 +518,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for external AI background removal when \'true\' is provided as a string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -533,7 +533,7 @@ describe("URL generation", function () { }); it('should not apply external AI background removal when value is not true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -548,7 +548,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when gradient transformation is provided as a string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -563,7 +563,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when gradient transformation is provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -578,7 +578,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when gradient transformation is set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -593,7 +593,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when AI drop shadow transformation is set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -608,7 +608,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -623,7 +623,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -638,7 +638,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when shadow transformation is set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -653,7 +653,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when shadow transformation is provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -668,7 +668,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -683,7 +683,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when sharpen transformation is set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -698,7 +698,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -713,7 +713,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when sharpen transformation is provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -728,7 +728,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when unsharpMask transformation is set to true', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -743,7 +743,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -758,7 +758,7 @@ describe("URL generation", function () { }); it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -773,7 +773,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for trim transformation when set to true (boolean)', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -788,7 +788,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for trim transformation when provided as an empty string', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -803,7 +803,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for trim transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -819,7 +819,7 @@ describe("URL generation", function () { // Width parameter tests it('should generate the correct URL for width transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -834,7 +834,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for width transformation when provided with a string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -849,7 +849,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -865,7 +865,7 @@ describe("URL generation", function () { // Height parameter tests it('should generate the correct URL for height transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -880,7 +880,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for height transformation when provided with a string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -895,7 +895,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -911,7 +911,7 @@ describe("URL generation", function () { // AspectRatio parameter tests it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -926,7 +926,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -941,7 +941,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -957,7 +957,7 @@ describe("URL generation", function () { // Background parameter tests it('should generate the correct URL for background transformation when provided with a solid color', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -972,7 +972,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for background transformation when provided with the blurred option', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -987,7 +987,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for background transformation when provided with the genfill option', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1003,7 +1003,7 @@ describe("URL generation", function () { // Crop parameter tests it('should generate the correct URL for crop transformation when provided with force value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1018,7 +1018,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for crop transformation when provided with at_max value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1034,7 +1034,7 @@ describe("URL generation", function () { // CropMode parameter tests it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1049,7 +1049,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for cropMode transformation when provided with extract value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1065,7 +1065,7 @@ describe("URL generation", function () { // Focus parameter tests it('should generate the correct URL for focus transformation when provided with a string value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1080,7 +1080,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for focus transformation when face detection is specified', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1096,7 +1096,7 @@ describe("URL generation", function () { // Quality parameter test it('should generate the correct URL for quality transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1112,7 +1112,7 @@ describe("URL generation", function () { // Coordinate parameters tests it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1127,7 +1127,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1142,7 +1142,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for xCenter transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1157,7 +1157,7 @@ describe("URL generation", function () { }); it('should generate the correct URL for yCenter transformation when provided with a number value', function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path1.jpg", @@ -1173,7 +1173,7 @@ describe("URL generation", function () { it('Including deprecated properties', function () { // This is just testing how the SDK constructs the URL, not actual valid transformations. - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", @@ -1219,7 +1219,7 @@ describe("URL generation", function () { it('should generate the correct URL with many transformations, including video and AI transforms', function () { // Example test with comprehensive transformations - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", transformationPosition: "query", src: "/test_path.jpg", diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index fccdbdc..0ade645 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -1,12 +1,12 @@ const chai = require("chai"); const expect = chai.expect; -import { buildURL } from "../../src/index"; +import { buildSrc } from "../../src/index"; import { safeBtoa } from "../../src/utils/transformation"; describe("Overlay Transformation Test Cases", function () { it('Ignore invalid values if text is missing', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -20,7 +20,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Ignore if type is missing', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -33,7 +33,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Ignore invalid values if input (image)', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -47,7 +47,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Ignore invalid values if input (video)', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -61,7 +61,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Ignore invalid values if input (subtitle)', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -75,7 +75,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Ignore invalid values if color is missing (solidColor)', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -89,7 +89,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Text overlay generates correct URL with encoded overlay text', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -104,7 +104,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Image overlay generates correct URL with input logo.png', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -119,7 +119,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-video.mp4", @@ -134,7 +134,7 @@ describe("Overlay Transformation Test Cases", function () { }); it("Subtitle overlay generates correct URL with input subtitle.srt", function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-video.mp4", @@ -149,7 +149,7 @@ describe("Overlay Transformation Test Cases", function () { }); it("Solid color overlay generates correct URL with background color FF0000", function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -164,7 +164,7 @@ describe("Overlay Transformation Test Cases", function () { }); it('Combined overlay transformations generate correct URL including nested overlays', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", src: "/base-image.jpg", @@ -308,7 +308,7 @@ describe("Overlay Transformation Test Cases", function () { describe("Overlay encoding test cases", function () { it('Nested simple path, should use i instead of ie, handle slash properly', function () { - const url = buildURL({ + const url = buildSrc({ // Using a different endpoint here, as we are checking for /demo transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", @@ -324,7 +324,7 @@ describe("Overlay encoding test cases", function () { }); it('Nested non-simple path, should use ie instead of i', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", @@ -339,7 +339,7 @@ describe("Overlay encoding test cases", function () { }); it('Simple text overlay, should use i instead of ie', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", @@ -354,7 +354,7 @@ describe("Overlay encoding test cases", function () { }); it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", @@ -369,7 +369,7 @@ describe("Overlay encoding test cases", function () { }); it('Non simple text overlay, should use ie instead of i', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/medium_cafe_B1iTdD0C.jpg", @@ -384,7 +384,7 @@ describe("Overlay encoding test cases", function () { }); it('Text overlay with explicit plain encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", @@ -400,7 +400,7 @@ describe("Overlay encoding test cases", function () { }); it('Text overlay with explicit base64 encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", @@ -416,7 +416,7 @@ describe("Overlay encoding test cases", function () { }); it('Image overlay with explicit plain encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", @@ -432,7 +432,7 @@ describe("Overlay encoding test cases", function () { }); it('Image overlay with explicit base64 encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", @@ -448,7 +448,7 @@ describe("Overlay encoding test cases", function () { }); it('Video overlay with explicit base64 encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", @@ -464,7 +464,7 @@ describe("Overlay encoding test cases", function () { }); it('Subtitle overlay with explicit plain encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", @@ -480,7 +480,7 @@ describe("Overlay encoding test cases", function () { }); it('Subtitle overlay with explicit base64 encoding', function () { - const url = buildURL({ + const url = buildSrc({ transformationPosition: "path", urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.mp4", @@ -496,7 +496,7 @@ describe("Overlay encoding test cases", function () { }); it("Avoid double encoding when transformation string is in query params", function () { - const url = buildURL({ + const url = buildSrc({ urlEndpoint: "https://ik.imagekit.io/demo", src: "/sample.jpg", transformation: [{ From c989e4a8507818e784fda15cb1296f627104e76d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 14:59:32 +0530 Subject: [PATCH 120/166] refactor: update package name to @imagekit/javascript and modify description in package.json --- package-lock.json | 4 ++-- package.json | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50d96c7..9bf6f47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "imagekit-javascript", + "name": "@imagekit/javascript", "version": "5.0.0.beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "imagekit-javascript", + "name": "@imagekit/javascript", "version": "5.0.0.beta.1", "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 0136fb9..7e88f09 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "imagekit-javascript", + "name": "@imagekit/javascript", "version": "5.0.0.beta.1", - "description": "Javascript SDK for using ImageKit.io in the browser", + "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", "browser": "dist/imagekit.min.js", @@ -55,14 +55,12 @@ "keywords": [ "imagekit", "javascript", - "sdk", - "js", "image", + "video", + "upload", "optimization", "transformation", "resize", - "upload", - "video", "overlay" ], "author": "ImageKit Developer", @@ -71,4 +69,4 @@ "url": "https://github.com/imagekit-developer/imagekit-javascript/issues" }, "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme" -} +} \ No newline at end of file From 0b1463e3d903eacc20e92d869c1f7f458f4cb376 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 15:01:14 +0530 Subject: [PATCH 121/166] chore: add publishConfig with beta tag in package.json --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 7e88f09..24cc67d 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,9 @@ "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" }, + "publishConfig": { + "tag": "beta" + }, "repository": { "type": "git", "url": "git+https://github.com/imagekit-developer/imagekit-javascript.git" From aae99349511d7637795460dbb733d412296d2a89 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 15:23:11 +0530 Subject: [PATCH 122/166] update readme --- README.md | 529 ++---------------------------------------------------- 1 file changed, 14 insertions(+), 515 deletions(-) diff --git a/README.md b/README.md index 1634536..50ecdc7 100644 --- a/README.md +++ b/README.md @@ -2,538 +2,37 @@ # ImageKit.io JavaScript SDK -![gzip size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=gzip&label=gzip) -![brotli size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=brotli&label=brotli) +![gzip size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=gzip&label=gzip) +![brotli size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=brotli&label=brotli) ![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) -[![npm version](https://img.shields.io/npm/v/imagekit-javascript)](https://www.npmjs.com/package/imagekit-javascript) +[![npm version](https://img.shields.io/npm/v/@imagekit/javascript)](https://www.npmjs.com/package/@imagekit/javascript) [![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) [![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) -A lightweight JavaScript SDK for generating image and video URLs with transformations, and for uploading files directly from the browser to ImageKit. This SDK is intended for use in the browser only. For Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). +This lightweight, dependency-free JavaScript SDK is designed specifically for browser use. It provides utility functions to generate image and video `src` URLs using [ImageKit transformations](https://imagekit.io/docs/transformations) and to upload files to the ImageKit media library. -## Table of Contents -- [Installation](#installation) -- [Initialization](#initialization) -- [URL Generation](#url-generation) - - [Basic URL Generation](#basic-url-generation) - - [Advanced URL Generation Examples](#advanced-url-generation-examples) - - [Chained Transformations](#chained-transformations) - - [Overlays and Effects](#overlays-and-effects) - - [AI and Advanced Transformations](#ai-and-advanced-transformations) - - [Arithmetic Expressions in Transformations](#arithmetic-expressions-in-transformations) - - [Supported Transformations](#supported-transformations) - - [Handling Unsupported Transformations](#handling-unsupported-transformations) -- [File Upload](#file-upload) - - [Basic Upload Example](#basic-upload-example) - - [Promise-based Upload Example](#promise-based-upload-example) -- [Test Examples](#test-examples) -- [Changelog](#changelog) +For server-side applications with Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). ## Installation -### Using npm -Install the SDK via npm: -```bash -npm install imagekit-javascript --save -# or -yarn add imagekit-javascript -``` - -Then import ImageKit: -```js -import ImageKit from "imagekit-javascript"; -// or with CommonJS: -const ImageKit = require("imagekit-javascript"); -``` - -### Using CDN -You can also use the global CDN: - -Download a specific version: -``` -https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js -``` -Or for the latest version, remove the version number (don't use in production as it may break your code if a new major version is released): -``` -https://unpkg.com/imagekit-javascript/dist/imagekit.min.js -``` - -And include it in your HTML: -```html - -``` - -## Initialization -To use the SDK, initialize it with your ImageKit URL endpoint. You can get the URL endpoint [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from the [developer section](https://imagekit.io/dashboard/developer/api-keys): - -```js -var imagekit = new ImageKit({ - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", // Required - transformationPosition: "query", // Optional, defaults to "query" - publicKey: "your_public_api_key", // Optional, required only for client-side file uploads -}); -``` - -### Initialization Options - -| Option | Description | Example | -| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | -| urlEndpoint | Required. Your ImageKit URL endpoint or custom domain. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | -| transformationPosition | Optional. Specifies whether transformations are added as URL path segments (`path`) or query parameters (`query`). The default is `query`, which allows you to perform wildcard purges and remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | -| publicKey | Optional. Your public API key for client-side uploads. | `publicKey: "your_public_api_key"` | - - -## URL Generation - -The SDK’s `.url()` method enables you to generate optimized image and video URLs with a variety of transformations. - -The method accepts an object with the following parameters: - -| Option | Description | Example | -| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | -| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | -| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. See [supported transformations](#supported-transformations). | `[ { width: 300, height: 400 } ]` | -| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | - -Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings for a specific `.url()` call. - -### Basic URL Generation - -*A simple height and width transformation:* - -```js -var imageURL = imagekit.url({ - path: "/default-image.jpg", - urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", - transformation: [{ - height: 300, - width: 400 - }] -}); -``` - -*Result Example:* -``` -https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,w-400 -``` - -SDK automatically generates the URL based on the provided parameters. The generated URL includes the base URL, path, and transformation parameters. - -### Advanced URL Generation Examples - -#### Chained Transformations -Apply multiple transformations by passing an array: -```js -var imageURL = imagekit.url({ - path: "/default-image.jpg", - transformation: [{ - height: 300, - width: 400 - }, { - rotation: 90 - }], -}); -``` - -*Result Example:* -``` -https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400:rt-90 -``` - -#### Overlays and Effects -*Text Overlay Example:* -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", - transformation: [{ - width: 400, - height: 300, - overlay: { - text: "Imagekit", - fontSize: 50, - color: "red", - position: { - x: 10, - y: 20 - } - } - }] -}); -``` - -*Image Overlay Example:* - -```js -var imageURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", - transformation: [{ - width: 400, - height: 300, - overlay: { - type: "image", - input: "logo.png", - transformation: [{ - width: 100, - border: "10_CDDC39" - }], - position: { - focus: "top_left" - } - } - }] -}); -``` - -*Video Overlay Example:* +You can install the SDK in your project using npm or yarn. -```js -var videoOverlayURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", - transformation: [{ - overlay: { - type: "video", - input: "overlay-video.mp4", - position: { - x: "10", - y: "20" - }, - timing: { - start: 5, - duration: 10 - } - } - }] -}); -``` - -*Subtitle Overlay Example:* - -```js -var subtitleOverlayURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", - transformation: [{ - overlay: { - type: "subtitle", - input: "subtitle.vtt", - transformation: [{ - fontSize: 16, - fontFamily: "Arial" - }], - position: { - focus: "bottom" - }, - timing: { - start: 0, - duration: 5 - } - } - }] -}); -``` - -*Solid Color Overlay Example:* -```js -var solidColorOverlayURL = imagekit.url({ - src: "https://ik.imagekit.io/your_imagekit_id/base-image.jpg", - transformation: [{ - overlay: { - type: "solidColor", - color: "FF0000", - transformation: [{ - width: 100, - height: 50, - alpha: 5 - }], - position: { x: 20, y: 20 } - } - }] -}); -``` - -##### Overlay Options - -ImageKit supports various overlay types, including text, image, video, subtitle, and solid color overlays. Each overlay type has specific configuration options to customize the overlay appearance and behavior. To learn more about how overlays work, refer to the [ImageKit documentation](https://imagekit.io/docs/transformations#overlay-using-layers). - -The table below outlines the available overlay configuration options: - -| Option | Description | Example | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| type | Specifies the type of overlay. Supported values: `text`, `image`, `video`, `subtitle`, `solidColor`. | `type: "text"` | -| text | (For text overlays) The text content to display. | `text: "ImageKit"` | -| input | (For image, video, or subtitle overlays) Relative path to the overlay asset. | `input: "logo.png"` or `input: "overlay-video.mp4"` | -| color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | -| encoding | Accepted values: `auto`, `plain`, `base64`. [Check this](#encoding-options) for more details. | `encoding: "auto"` | -| transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | -| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | -| timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | - -##### Encoding Options - -Overlay encoding options define how the overlay input is converted for URL construction. When set to `auto`, the SDK automatically determines whether to use plain text or Base64 encoding based on the input content. - -For text overlays: -- If `auto` is used, the SDK checks the text overlay input: if it is URL-safe, it uses the format `i-{input}` (plain text); otherwise, it applies Base64 encoding with the format `ie-{base64_encoded_input}`. -- You can force a specific method by setting encoding to `plain` (always use `i-{input}`) or `base64` (always use `ie-{base64}`). -- Note: In all cases, the text is percent-encoded to ensure URL safety. - -For image, video, and subtitle overlays: -- The input path is processed by removing any leading/trailing slashes and replacing inner slashes with `@@` when `plain` is used. -- Similarly, if `auto` is used, the SDK determines whether to apply plain text or Base64 encoding based on the characters present. -- For explicit behavior, use `plain` or `base64` to enforce the desired encoding. - -Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or `base64` when a specific encoding method is required. - -##### Solid Color Overlay Transformations - -| Option | Description | Example | -| ------ | ---------------------------------------------------------------------------------------------------------------------------------- | --------------- | -| width | Specifies the width of the solid color overlay block (in pixels or as an arithmetic expression). | `width: 100` | -| height | Specifies the height of the solid color overlay block (in pixels or as an arithmetic expression). | `height: 50` | -| radius | Specifies the corner radius of the solid color overlay block or shape. Can be a number or `"max"` for circular/oval shapes. | `radius: "max"` | -| alpha | Specifies the transparency level of the solid color overlay. Supports integers from 1 (most transparent) to 9 (least transparent). | `alpha: 5` | - -##### Text Overlay Transformations - -| Option | Description | Example | -| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | -| width | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions are supported (e.g., `bw_mul_0.2` or `bh_div_2`). | `width: 400` | -| fontSize | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | -| fontFamily | Specifies the font family of the overlaid text. Choose from the supported fonts or provide a custom font. | `fontFamily: "Arial"` | -| fontColor | Specifies the font color of the overlaid text. Accepts an RGB hex code, an RGBA code, or a standard color name. | `fontColor: "FF0000"` | -| innerAlignment | Specifies the inner alignment of the text when it doesn’t occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | -| padding | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | -| alpha | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | -| typography | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | -| background | Specifies the background color of the text overlay. Accepts an RGB hex code, an RGBA code, or a color name. | `background: "red"` | -| radius | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` for circular/oval shape. | `radius: "max"` | -| rotation | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | -| flip | Specifies the flip option for the text overlay. Supported values: `h`, `v`, `h_v`, `v_h`. | `flip: "h"` | -| lineHeight | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | - -##### Subtitle Overlay Transformations - -| Option | Description | Example | -| ----------- | --------------------------------------------------------------------------------------------------------- | ----------------------- | -| background | Specifies the subtitle background color using a standard color name, RGB color code, or RGBA color code. | `background: "blue"` | -| fontSize | Sets the font size of subtitle text. | `fontSize: 16` | -| fontFamily | Sets the font family of subtitle text. | `fontFamily: "Arial"` | -| color | Specifies the font color of subtitle text using standard color name, RGB, or RGBA color code. | `color: "FF0000"` | -| typography | Sets the typography style of subtitle text. Supported values: `b`, `i`, `b_i`. | `typography: "b"` | -| fontOutline | Specifies the font outline for subtitles. Requires an outline width and color separated by an underscore. | `fontOutline: "2_blue"` | -| fontShadow | Specifies the font shadow for subtitles. Requires shadow color and indent separated by an underscore. | `fontShadow: "blue_2"` | - -#### AI and Advanced Transformations -*Background Removal:* -```js -var imageURL = imagekit.url({ - path: "/sample-image.jpg", - transformation: [{ - aiRemoveBackground: true - }] -}); -``` -*Upscaling:* -```js -var upscaledURL = imagekit.url({ - path: "/sample-image.jpg", - transformation: [{ - aiUpscale: true - }] -}); -``` -*Drop Shadow:* -```js -var dropShadowURL = imagekit.url({ - path: "/sample-image.jpg", - transformation: [{ - aiDropShadow: "az-45" - }] -}); -``` - -#### Arithmetic Expressions in Transformations -```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" - }] -}); -``` - -### Supported Transformations - -The SDK gives a name to each transformation parameter (e.g. `height` maps to `h`, `width` maps to `w`). If the property does not match any of the following supported options, it is added as is. - -If you want to generate transformations without any modifications, use the `raw` parameter. - -Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. - -| Transformation Name | URL Parameter | -| -------------------------- | ---------------------------------------------------------------------------------------------- | -| width | w | -| height | h | -| aspectRatio | ar | -| quality | q | -| aiRemoveBackground | e-bgremove (ImageKit powered) | -| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | -| aiUpscale | e-upscale | -| aiRetouch | e-retouch | -| aiVariation | e-genvar | -| aiDropShadow | e-dropshadow | -| aiChangeBackground | e-changebg | -| crop | c | -| cropMode | cm | -| x | x | -| y | y | -| xCenter | xc | -| yCenter | yc | -| focus | fo | -| format | f | -| radius | r | -| background | bg | -| border | b | -| rotation | rt | -| blur | bl | -| named | n | -| dpr | dpr | -| progressive | pr | -| lossless | lo | -| trim | t | -| metadata | md | -| colorProfile | cp | -| defaultImage | di | -| original | orig | -| videoCodec | vc | -| audioCodec | ac | -| grayscale | e-grayscale | -| contrastStretch | e-contrast | -| shadow | e-shadow | -| sharpen | e-sharpen | -| unsharpMask | e-usm | -| gradient | e-gradient | -| flip | fl | -| opacity | o | -| zoom | z | -| page | pg | -| startOffset | so | -| endOffset | eo | -| duration | du | -| streamingResolutions | sr | -| overlay | Generates the correct layer syntax for image, video, text, subtitle, and solid color overlays. | -| raw | The string provided in raw will be added in the URL as is. | - -### Handling Unsupported Transformations - -If you specify a transformation parameter that is not explicitly supported by the SDK, it is added “as-is” in the generated URL. This provides flexibility for using new or custom transformations without waiting for an SDK update. - -For example: -```js -var imageURL = imagekit.url({ - path: "/test_path.jpg", - transformation: [{ - newparam: "cool" - }] -}); -// Generated URL: https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=newparam-cool +```bash +npm install @imagekit/javascript ``` -## File Upload - -The SDK offers a simple interface via the `.upload()` method to upload files to the ImageKit Media Library. This method requires the following: -- **file** (mandatory) -- **fileName** (mandatory) -- Security parameters: **signature**, **token**, and **expire** - -Before invoking the upload, generate the necessary security parameters as per the [ImageKit Upload API documentation](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). +## TypeScript support -### Upload Options -| Option | Description | Example | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | -| file | The file content to be uploaded. Accepts binary, base64 string, or URL. | `file: fileInput.files[0]` | -| fileName | The name to assign to the uploaded file. Supports alphanumeric characters, dot, underscore, and dash. | `fileName: "myImage.jpg"` | -| signature | HMAC-SHA1 digest computed using the private API key. Must be calculated on the server side. | `signature: "generated_signature"` | -| token | A unique token to prevent duplicate upload retries. Typically a V4 UUID or similar unique string. | `token: "unique_upload_token"` | -| expire | Unix timestamp (in seconds) indicating the signature expiry time (should be within 1 hour). | `expire: 1616161616` | -| useUniqueFileName | Boolean flag to automatically generate a unique filename if set to true. Defaults to true. | `useUniqueFileName: true` | -| folder | The folder path where the file will be uploaded. Automatically creates nested folders if they don’t exist. | `folder: "/images/uploads"` | -| isPrivateFile | Boolean to mark the file as private, restricting access to the original file URL. Defaults to false. | `isPrivateFile: false` | -| tags | Tags to associate with the file. Can be a comma-separated string or an array of tags. | `tags: "summer,holiday"` or `tags: ["summer","holiday"]` | -| customCoordinates | Specifies an area of interest in the image formatted as `x,y,width,height`. | `customCoordinates: "10,10,100,100"` | -| responseFields | Comma-separated list of fields to include in the upload response. | `responseFields: "tags,customCoordinates"` | -| extensions | Array of extension objects for additional image processing. | `extensions: [{ name: "auto-tagging" }]` | -| webhookUrl | URL to which the final status of extension processing will be sent. | `webhookUrl: "https://example.com/webhook"` | -| overwriteFile | Boolean flag indicating whether to overwrite a file if it exists. Defaults to true. | `overwriteFile: true` | -| overwriteAITags | Boolean flag to remove AITags from a file if overwritten. Defaults to true. | `overwriteAITags: true` | -| overwriteTags | Boolean flag that determines if existing tags should be removed when new tags are not provided. Defaults to true when file is overwritten without tags. | `overwriteTags: true` | -| overwriteCustomMetadata | Boolean flag dictating if existing custom metadata should be removed when not provided. Defaults to true under similar conditions as tags. | `overwriteCustomMetadata: true` | -| customMetadata | Stringified JSON or an object containing custom metadata key-value pairs to associate with the file. | `customMetadata: {author: "John Doe"}` | -| transformation | Optional transformation object to apply during the upload process. It follows the same structure as in URL generation. | `transformation: { pre: "w-200,h-200", post: [...] }` | -| xhr | An optional XMLHttpRequest object provided to monitor upload progress. | `xhr: new XMLHttpRequest()` | -| checks | Optional string value for specifying server-side checks to run before file upload. | `checks: "file.size' < '1MB'"` | +The SDK is written in TypeScript, offering first-class TypeScript support. Enjoy excellent type safety and IntelliSense in your IDE. You can use it in your TypeScript projects without any additional configuration. -### Basic Upload Example -Below is an HTML form example that uses a callback for handling the upload response: - -```html -
- - -
- - -``` - -### Promise-based Upload Example - -You can also use promises for a cleaner asynchronous approach: -```js -imagekit.upload({ - file: file.files[0], - fileName: "abc1.jpg", - token: 'generated_token', - signature: 'generated_signature', - expire: 'generated_expire' -}).then(result => { - console.log(result); -}).catch(error => { - console.error(error); -}); -``` +To enable type checking in JavaScript projects, add `//@ts-check` at the top of your JavaScript files. This will activate type checking in your IDE. -## Test Examples +## Documentation -For a quick demonstration of the SDK features, check the test suite: -- URL generation examples can be found in [basic.js](./test/url-generation/basic.js) and [overlay.js](./test/url-generation/overlay.js) files. -- File upload examples can be found in [test/upload.js](./test/upload.js). +Refer to the ImageKit [official documentation](http://localhost/docs/integration/javascript) for more details on how to use the SDK. ## Changelog -For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). \ No newline at end of file +For a detailed history of changes, refer to [CHANGELOG.md](CHANGELOG.md). From b7018e2efe326f384cec0a3fe96a91b0549cebad Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 15:55:12 +0530 Subject: [PATCH 123/166] refactor: rename signal to abortSignal in upload options and update JSDoc --- src/interfaces/SrcOptions.ts | 16 +-- src/interfaces/Transformation.ts | 93 ++++++++--------- src/interfaces/UploadOptions.ts | 169 ++++++++++++++++++------------- src/upload.ts | 48 +++++---- src/url.ts | 16 ++- test/upload.js | 6 +- 6 files changed, 199 insertions(+), 149 deletions(-) diff --git a/src/interfaces/SrcOptions.ts b/src/interfaces/SrcOptions.ts index bf8f914..4b0187e 100644 --- a/src/interfaces/SrcOptions.ts +++ b/src/interfaces/SrcOptions.ts @@ -3,7 +3,8 @@ import { TransformationPosition } from "."; export interface SrcOptions { /** - * Accepts relative or absolute path of the resource. If relative path is provided, it is appended to the `urlEndpoint`. If absolute path is provided, `urlEndpoint` is ignored. + * Accepts a relative or absolute path of the resource. If a relative path is provided, it is appended to the `urlEndpoint`. + * If an absolute path is provided, `urlEndpoint` is ignored. */ src: string; @@ -20,14 +21,15 @@ export interface SrcOptions { transformation?: Array; /** - * 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. + * These are additional query parameters that you want to add to the final URL. + * They can be any query parameters and not necessarily related to ImageKit. + * This is especially useful if you want to add a versioning parameter to your URLs. */ queryParameters?: { [key: string]: string | number }; /** - * By default, the transformation string is added as a query parameter in the URL e.g. `?tr=w-100,h-100`. If you want to add the transformation string in the path of the URL, set this to `path`. - */ + * By default, the transformation string is added as a query parameter in the URL, e.g., `?tr=w-100,h-100`. + * If you want to add the transformation string in the path of the URL, set this to `path`. + */ transformationPosition?: TransformationPosition; -} \ No newline at end of file +} diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index adbe8dd..5d3fc99 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -4,29 +4,30 @@ export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440 /** * The SDK provides easy-to-use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. - * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, you can use the `raw` parameter to pass the transformation string directly. + * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, + * You can use the `raw` parameter to pass the transformation string directly. * * {@link https://imagekit.io/docs/transformations|Transformations Documentation} */ export interface Transformation { /** - * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage - * (e.g., `0.4` represents 40% of the original width). You can also supply arithmetic expressions (e.g., `iw_div_2`). + * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.4` represents 40% of the original width). + * You can also supply arithmetic expressions (e.g., `iw_div_2`). * * Width transformation - {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#width---w|Videos} */ width?: number | string; /** - * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage - * (e.g., `0.5` represents 50% of the original height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). + * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.5` represents 50% of the original height). + * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). * * Height transformation - {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#height---h|Videos} */ height?: number | string; /** - * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). + * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. * * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar|Image Resize and Crop - Aspect Ratio} @@ -34,7 +35,7 @@ export interface Transformation { aspectRatio?: number | string; /** - * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. + * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. * * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background|Effects and Enhancements - Solid Color Background} @@ -51,7 +52,7 @@ export interface Transformation { background?: string; /** - * Adds a border to the output media. Accepts a string in the format `_` + * Adds a border to the output media. Accepts a string in the format `_` * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. * * {@link https://imagekit.io/docs/effects-and-enhancements#border---b|Effects and Enhancements - Border} @@ -76,14 +77,14 @@ export interface Transformation { dpr?: number /** - * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. + * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. * * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo|Image Resize and Crop - Focus} */ focus?: string; /** - * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. + * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. * * {@link https://imagekit.io/docs/image-optimization#quality---q|Image Optimization - Quality} @@ -111,7 +112,7 @@ export interface Transformation { yCenter?: number | string; /** - * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. + * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. * You can also pass `orig` for images to return the original format. * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. * @@ -120,28 +121,28 @@ export interface Transformation { format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; /** - * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. + * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. * * {@link https://imagekit.io/docs/video-optimization#video-codec---vc|Video Optimization - Video Codec} */ videoCodec?: "h264" | "vp9" | "av1" | "none"; /** - * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. + * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. * * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac|Video Optimization - Audio Codec} */ audioCodec?: "aac" | "opus" | "none"; /** - * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. + * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. * * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r|Effects and Enhancements - Radius} */ radius?: number | "max"; /** - * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation + * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation * or `auto` to use the orientation specified in the image's EXIF data. * For videos, only the following values are supported: 0, 90, 180, 270, or 360. * @@ -150,7 +151,7 @@ export interface Transformation { rotation?: number | string; /** - * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. + * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. * * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl|Effects and Enhancements - Blur} */ @@ -162,14 +163,14 @@ export interface Transformation { named?: string; /** - * Specifies a fallback image if the resource is not found, e.g., a URL or file path. + * Specifies a fallback image if the resource is not found, e.g., a URL or file path. * * {@link https://imagekit.io/docs/image-transformation#default-image---di|Image Transformation - Default Image} */ defaultImage?: string; /** - * Flips or mirrors an image either horizontally, vertically, or both. + * Flips or mirrors an image either horizontally, vertically, or both. * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. * * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl|Effects and Enhancements - Flip} @@ -177,14 +178,14 @@ export interface Transformation { flip?: "h" | "v" | "h_v" | "v_h"; /** - * If set to true, serves the original file without applying any transformations. + * If set to true, serves the original file without applying any transformations. * * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true|Core Delivery Features - Deliver Original File As Is} */ original?: boolean; /** - * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. * Arithmetic expressions are also supported. * * {@link https://imagekit.io/docs/trim-videos#start-offset---so|Trim Videos - Start Offset} @@ -192,7 +193,7 @@ export interface Transformation { startOffset?: number | string; /** - * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. * Typically used with startOffset to define a time window. Arithmetic expressions are supported. * * {@link https://imagekit.io/docs/trim-videos#end-offset---eo|Trim Videos - End Offset} @@ -200,7 +201,7 @@ export interface Transformation { endOffset?: number | string; /** - * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. * * {@link https://imagekit.io/docs/trim-videos#duration---du|Trim Videos - Duration} @@ -208,35 +209,35 @@ export interface Transformation { duration?: number | string; /** - * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. + * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. * * {@link https://imagekit.io/docs/adaptive-bitrate-streaming|Adaptive Bitrate Streaming} */ streamingResolutions?: StreamingResolution[]; /** - * Enables a grayscale effect for images. + * Enables a grayscale effect for images. * * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale|Effects and Enhancements - Grayscale} */ grayscale?: true; /** - * Upscales images beyond their original dimensions using AI. Not supported inside overlay. + * Upscales images beyond their original dimensions using AI. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} */ aiUpscale?: true /** - * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. + * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} */ aiRetouch?: true /** - * Generates a variation of an image using AI. This produces a new image with slight variations from the original, + * Generates a variation of an image using AI. This produces a new image with slight variations from the original, * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} @@ -244,7 +245,7 @@ export interface Transformation { aiVariation?: true /** - * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. + * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. * Supported inside overlay. @@ -254,7 +255,7 @@ export interface Transformation { aiDropShadow?: true | string /** - * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, + * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. * Not supported inside overlay. * @@ -263,7 +264,7 @@ export interface Transformation { aiChangeBackground?: string; /** - * Applies ImageKit’s in-house background removal. + * Applies ImageKit’s in-house background removal. * Supported inside overlay. * * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} @@ -271,7 +272,7 @@ export interface Transformation { aiRemoveBackground?: true /** - * Uses third-party background removal. + * Uses third-party background removal. * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. * Supported inside overlay. * @@ -280,14 +281,14 @@ export interface Transformation { aiRemoveBackgroundExternal?: true /** - * Automatically enhances the contrast of an image (contrast stretch). + * Automatically enhances the contrast of an image (contrast stretch). * * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast|Effects and Enhancements - Contrast Stretch} */ contrastStretch?: true /** - * Adds a shadow beneath solid objects in an image with a transparent background. + * Adds a shadow beneath solid objects in an image with a transparent background. * For AI-based drop shadows, refer to aiDropShadow. * Pass `true` for a default shadow, or provide a string for a custom shadow. * @@ -296,7 +297,7 @@ export interface Transformation { shadow?: true | string /** - * Sharpens the input image, highlighting edges and finer details. + * Sharpens the input image, highlighting edges and finer details. * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. * * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} @@ -304,7 +305,7 @@ export interface Transformation { sharpen?: true | number /** - * Applies Unsharp Masking (USM), an image sharpening technique. + * Applies Unsharp Masking (USM), an image sharpening technique. * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. * * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} @@ -312,14 +313,14 @@ export interface Transformation { unsharpMask?: true | string; /** - * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. + * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. * * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} */ gradient?: true | string; /** - * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, + * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, * pixelated version of the full image, which gradually improves to provide a faster perceived load time. * * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr|Image Optimization - Progressive Image} @@ -327,21 +328,21 @@ export interface Transformation { progressive?: boolean; /** - * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. + * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. * * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo|Image Optimization - Lossless Compression} */ lossless?: boolean /** - * Indicates whether the output image should retain the original color profile. + * Indicates whether the output image should retain the original color profile. * * {@link https://imagekit.io/docs/image-optimization#color-profile---cp|Image Optimization - Color Profile} */ colorProfile?: boolean; /** - * By default, ImageKit removes all metadata during automatic image compression. + * By default, ImageKit removes all metadata during automatic image compression. * Set this to true to preserve metadata. * * {@link https://imagekit.io/docs/image-optimization#image-metadata---md|Image Optimization - Image Metadata} @@ -349,14 +350,14 @@ export interface Transformation { metadata?: boolean; /** - * Specifies the opacity level of the output image. + * Specifies the opacity level of the output image. * * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o|Effects and Enhancements - Opacity} */ opacity?: number; /** - * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, + * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, * leaving only the central object in the output image. * * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t|Effects and Enhancements - Trim Edges} @@ -364,7 +365,7 @@ export interface Transformation { trim?: true | number; /** - * Accepts a numeric value that determines how much to zoom in or out of the cropped area. + * Accepts a numeric value that determines how much to zoom in or out of the cropped area. * It should be used in conjunction with fo-face or fo-. * * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z|Image Resize and Crop - Zoom} @@ -372,7 +373,7 @@ export interface Transformation { zoom?: number; /** - * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). + * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), * or by name (e.g., `name-layer-4` for a PSD layer). * @@ -381,14 +382,14 @@ export interface Transformation { page?: number | string; /** - * Pass any transformation not directly supported by the SDK. + * Pass any transformation not directly supported by the SDK. * This transformation string is appended to the URL as provided. */ raw?: string; /** - * Specifies an overlay to be applied on the parent image or video. + * Specifies an overlay to be applied on the parent image or video. * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. * * {@link https://imagekit.io/docs/transformations#overlay-using-layers|Transformations - Overlay Using Layers} diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index cd22f9d..7c4a2dd 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -17,46 +17,61 @@ interface AbsObject { type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; interface Transformation { - pre?: string - post?: PostTransformation[] + /** + * Specifies pre-transformations to be applied. Must be a valid string of transformations like "w-300,h-300". + * Refer to the docs for more details on transformations. + */ + pre?: string; + + /** + * Specifies post-transformations to be applied. This is an array of transformation objects, each with: + * - type: One of "transformation", "gif-to-video", "thumbnail", or "abs". + * - value: A valid transformation string required if "type" is "transformation" or "abs". Optional if "type" is "gif-to-video" or "thumbnail". + * - protocol: Used only when type is "abs". Can be "hls" or "dash". + * + * Refer to the docs for more details on transformations and usage in post. + */ + post?: PostTransformation[]; } + /** - * Options used when uploading a file. Checkout [upload docs](https://imagekit.io/docs/api-reference/upload-file/upload-file#Request) for more details. + * Options used when uploading a file using the V1 API. + * Check out the official documentation: + * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file} */ 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. + * This field accepts three main input formats for the file content: + * - "binary": Directly pass the binary data. Typically used when uploading via the browser using a File or Blob. + * - "base64": A base64-encoded string of the file content. + * - "url": A direct URL from which ImageKit server will download the file and upload. */ file: string | Blob | File; /** - * The name with which the file has to be uploaded. - * The file name 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 _ - */ + * The name with which the file should be uploaded. + * - Valid characters: alphanumeric (a-z, A-Z, 0-9, including Unicode letters and numerals) and the special chars ". _ -" + * - Any other character (including space) is replaced with "_" + * + * Example: "company_logo.png" + */ fileName: string; /** - * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. This should be in lowercase. - * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. + * The HMAC-SHA1 digest of the concatenation of "token + expire". The signing key is your ImageKit private API key. + * Required for client-side authentication. Must be computed on the server side. + * Calculate this signature in your secure server and pass it to the client. */ signature: string; /** - * A unique value generated by the client, which will be used by the ImageKit.io server 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. - * 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. + * A unique value to identify and prevent replays. Typically a UUID (e.g., version 4). + * Each request must carry a fresh token. The server rejects reused tokens, even if the original request failed. */ token: string; /** - * The time until your signature is valid. It must be a Unix time in less than 1 hour into the future. It should be in seconds. + * A Unix timestamp in seconds, less than 1 hour in the future. */ expire: number; @@ -73,85 +88,94 @@ export interface UploadOptions { * 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. + * Optionally set tags on the uploaded file. + * If passing an array, the SDK automatically joins them into a comma-separated string when sending to the server. + * Example: ["t-shirt", "round-neck", "men"] => "t-shirt,round-neck,men" */ 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 (including unicode letters, marks, and numerals in other languages) - * - Special Characters: / _ and - - * - Using multiple / creates a nested folder. - * Default value - / + * The folder path where the file will be stored, e.g., "/images/folder/". + * - If the path doesn't exist, it is created on-the-fly. + * - Nested folders are supported by using multiple "/". + * - Default: "/" */ 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 + * Whether to mark the file as private (only relevant for image uploads). + * A private file requires signed URLs or named transformations for access. + * Default: 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. + * A string in "x,y,width,height" format that defines the region of interest in an image (top-left coords and area size). + * Example: "10,10,100,100". */ 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. + * A comma-separated or array-based set of fields to return in the upload response. + * Example: "tags,customCoordinates,isPrivateFile,metadata" */ responseFields?: string | string[]; - /* - * Object with array of extensions to be processed on the image. + + /** + * An array of extension objects to apply to the image, e.g. background removal, auto-tagging, etc. + * The SDK will JSON-stringify this array automatically before sending. */ extensions?: object[]; - /* - * Final status of pending extensions will be sent to this URL. + + /** + * A webhook URL to receive the final status of any pending extensions once they've completed processing. */ - webhookUrl?: string - /* - * Default is true. If overwriteFile is set to false and useUniqueFileName is also false, and a file already exists at the exact location, upload API will return an error immediately. + webhookUrl?: string; + + /** + * Defaults to true. If false, and "useUniqueFileName" is also false, the API immediately fails if a file with the same name/folder already exists. */ - overwriteFile?: boolean - /* - * Default is true. 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. + overwriteFile?: boolean; + + /** + * Defaults to true. If true, and an existing file is found at the same location, its AITags are removed. Set to false to keep existing AITags. */ - overwriteAITags?: boolean - /* - * Default is true. If the request does not have tags , overwriteTags is set to true and a file already exists at the exact location, existing tags will be removed. - * In case the request body has tags, setting overwriteTags to false has no effect and request's tags are set on the asset. + overwriteAITags?: boolean; + + /** + * Defaults to true. If no tags are specified in the request, existing tags are removed from overwritten files. Setting to false has no effect if the request includes tags. */ - overwriteTags?: boolean - /* - * Default is true. If the request does not have customMetadata , overwriteCustomMetadata is set to true and a file already exists at the exact location, exiting customMetadata will be removed. - * In case the request body has customMetadata, setting overwriteCustomMetadata to false has no effect and request's customMetadata is set on the asset. + overwriteTags?: boolean; + + /** + * Defaults to true. If no customMetadata is specified in the request, existing customMetadata is removed from overwritten files. Setting to false has no effect if the request specifies customMetadata. */ - overwriteCustomMetadata?: boolean - /* - * Stringified JSON key-value data to be associated with the asset. Checkout overwriteCustomMetadata parameter to understand default behaviour. - * Before setting any custom metadata on an asset you have to create the field using custom metadata fields API. + overwriteCustomMetadata?: boolean; + + /** + * A stringified JSON or an object containing custom metadata fields to store with the file. + * Custom metadata fields must be pre-defined in your ImageKit configuration. */ - customMetadata?: string | Record> + customMetadata?: string | Record>; - transformation?: Transformation + /** + * Defines pre and post transformations to be applied to the file during upload. The SDK enforces certain validation rules for pre/post transformations. + * For details, see: + * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload} + */ + transformation?: Transformation; /** - * Optional XMLHttpRequest object that you can send for upload API request. You can listen to `progress` and other events on this object for any custom logic. + * An optional XMLHttpRequest instance for the upload. The SDK merges it with its own logic to handle progress events, etc. + * You can listen to `progress` or other events on this object for custom logic. */ - xhr?: XMLHttpRequest + xhr?: XMLHttpRequest; /** - * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. + * A string specifying the checks to be performed server-side before uploading to the media library, e.g. size or mime type checks. + * For format details, see: {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#upload-api-checks} */ checks?: string; @@ -161,7 +185,8 @@ export interface UploadOptions { onProgress?: (event: ProgressEvent) => void; /** - * Optional AbortSignal object that can be used to abort the upload request + * An AbortSignal instance that can be used to cancel the request if needed. + * When aborted, the request fails with an ImageKitAbortError. */ - signal?: AbortSignal; + abortSignal?: AbortSignal; } diff --git a/src/upload.ts b/src/upload.ts index a601094..c602894 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,6 +1,9 @@ import errorMessages from "./constants/errorMessages"; import { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; +/** + * Represents an error when a request to ImageKit is invalid. + */ export class ImageKitInvalidRequestError extends Error { /** * Optional metadata about the response. It is only available if server returns a response. @@ -13,6 +16,9 @@ export class ImageKitInvalidRequestError extends Error { } } +/** + * Represents an error when an upload operation is aborted. + */ export class ImageKitAbortError extends Error { /** * The reason why the operation was aborted, which can be any JavaScript value. If not specified, the reason is set to "AbortError" DOMException. @@ -25,6 +31,9 @@ export class ImageKitAbortError extends Error { } } +/** + * Represents a network error during an upload operation to ImageKit. + */ export class ImageKitUploadNetworkError extends Error { constructor(message: string) { super(message); @@ -32,6 +41,9 @@ export class ImageKitUploadNetworkError extends Error { } } +/** + * Represents a server error from ImageKit during an upload operation. + */ export class ImageKitServerError extends Error { /** * Optional metadata about the response. It is only available if server returns a response. @@ -45,19 +57,17 @@ export class ImageKitServerError extends Error { } /** - * Uploads a file to ImageKit with the given upload options. - * - * @throws {@link ImageKitInvalidRequestError} If the request is invalid. - * @throws {@link ImageKitAbortError} If the request is aborted. - * @throws {@link ImageKitUploadNetworkError} If there is a network error. - * @throws {@link ImageKitServerError} If there is a server error. - * + * Uploads a file to ImageKit with the given upload options. This function uses V1 API, check the [API docs](https://imagekit.io/docs/api-reference/upload-file/upload-file) for more details. + * + * @throws {ImageKitInvalidRequestError} If the request is invalid. + * @throws {ImageKitAbortError} If the request is aborted. + * @throws {ImageKitUploadNetworkError} If there is a network error. + * @throws {ImageKitServerError} If there is a server error. + * * @param {UploadOptions} uploadOptions - The options for uploading the file. - * @returns A Promise resolving to a successful {@link UploadResponse} + * @returns {Promise} A Promise resolving to a successful UploadResponse. */ -export const upload = ( - uploadOptions: UploadOptions -): Promise => { +export const upload = (uploadOptions: UploadOptions): Promise => { if(!uploadOptions) { return Promise.reject(new ImageKitInvalidRequestError("Invalid options provided for upload")); } @@ -139,7 +149,7 @@ export const upload = ( } else if (key === 'checks' && uploadOptions.checks) { formData.append("checks", uploadOptions.checks); } else if (uploadOptions[key] !== undefined) { - if (["onProgress", "signal"].includes(key)) continue; + if (["onProgress", "abortSignal"].includes(key)) continue; formData.append(key, String(uploadOptions[key])); } } @@ -157,27 +167,27 @@ export const upload = ( xhr.abort(); return reject(new ImageKitAbortError( "Upload aborted", - uploadOptions.signal?.reason + uploadOptions.abortSignal?.reason )); } - if (uploadOptions.signal) { - if (uploadOptions.signal.aborted) { + if (uploadOptions.abortSignal) { + if (uploadOptions.abortSignal.aborted) { // If the signal is already aborted, return immediately with the reason return reject(new ImageKitAbortError( "Upload aborted", - uploadOptions.signal?.reason + uploadOptions.abortSignal?.reason )); } // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted - uploadOptions.signal.addEventListener("abort", onAbortHandler); + uploadOptions.abortSignal.addEventListener("abort", onAbortHandler); // On XHR completion (success, fail, or abort), remove just this abort handler xhr.addEventListener("loadend", () => { - if (uploadOptions.signal) { - uploadOptions.signal.removeEventListener("abort", onAbortHandler); + if (uploadOptions.abortSignal) { + uploadOptions.abortSignal.removeEventListener("abort", onAbortHandler); } }); } diff --git a/src/url.ts b/src/url.ts index 8e98e95..c256af1 100644 --- a/src/url.ts +++ b/src/url.ts @@ -25,7 +25,13 @@ function pathJoin(parts: string[], sep?: string) { return parts.join(separator).replace(replace, separator); } -export const buildSrc = (opts: SrcOptions) => { +/** + * Builds a source URL with the given options. + * + * @param {SrcOptions} opts - The options for building the source URL. + * @returns {string} The constructed source URL. + */ +export const buildSrc = (opts: SrcOptions): string => { opts.urlEndpoint = opts.urlEndpoint || ""; opts.src = opts.src || ""; opts.transformationPosition = opts.transformationPosition || "query"; @@ -218,7 +224,13 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined return entries.join(transformationUtils.getTransformDelimiter()); } -export const buildTransformationString = function (transformation: Transformation[] | undefined) { +/** + * Builds a transformation string from the given transformations. + * + * @param {Transformation[] | undefined} transformation - The transformations to apply. + * @returns {string} The constructed transformation string. + */ +export const buildTransformationString = function (transformation: Transformation[] | undefined): string { if (!Array.isArray(transformation)) { return ""; } diff --git a/test/upload.js b/test/upload.js index b198dda..5018330 100644 --- a/test/upload.js +++ b/test/upload.js @@ -1297,7 +1297,7 @@ describe("File upload", async function () { ...securityParameters, fileName: "test_file_name", file: "test_file", - signal: abortController.signal + abortSignal: abortController.signal }; const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); @@ -1318,7 +1318,7 @@ describe("File upload", async function () { ...securityParameters, fileName: "test_file_name", file: "test_file", - signal: abortController.signal + abortSignal: abortController.signal }; const uploadPromise = upload(fileOptions); expect(server.requests.length).to.be.equal(1); @@ -1341,7 +1341,7 @@ describe("File upload", async function () { ...securityParameters, fileName: "test_file_name", file: "test_file", - signal: abortController.signal + abortSignal: abortController.signal }; try { await upload(fileOptions); From 200fb8faad12b96ca7d14ac635067dc41169416e Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 15:57:55 +0530 Subject: [PATCH 124/166] fix: update documentation link to point to the live ImageKit site --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 50ecdc7..dfe276c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ To enable type checking in JavaScript projects, add `//@ts-check` at the top of ## Documentation -Refer to the ImageKit [official documentation](http://localhost/docs/integration/javascript) for more details on how to use the SDK. +Refer to the ImageKit [official documentation](https://imagekit.io/docs/integration/javascript) for more details on how to use the SDK. ## Changelog From a7655f49c84e2b31acc63930a8090011942b78c7 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 16:17:02 +0530 Subject: [PATCH 125/166] refactor: re-export all interfaces for improved TypeDoc documentation --- src/interfaces/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 9e2f4c7..5aacf10 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,7 +1,7 @@ -import { Transformation, TransformationPosition } from "./Transformation"; -import { UploadOptions } from "./UploadOptions"; -import { ResponseMetadata, UploadResponse } from "./UploadResponse"; -import { SrcOptions } from "./SrcOptions"; - -export type { ResponseMetadata, Transformation, TransformationPosition, UploadOptions, UploadResponse, SrcOptions }; +// src/interfaces/index.ts +// Re-export all interfaces so that TypeDoc includes referenced types in the documentation +export * from './UploadResponse'; +export * from './UploadOptions'; +export * from './Transformation'; +export * from './SrcOptions'; From 7e3d812ecc8cba821c0c2f36c12df6a94cd54db7 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 16:24:29 +0530 Subject: [PATCH 126/166] fix links in JSDocs --- src/interfaces/Transformation.ts | 118 +++++++++++++++---------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 5d3fc99..774773b 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -7,14 +7,14 @@ export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440 * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, * You can use the `raw` parameter to pass the transformation string directly. * - * {@link https://imagekit.io/docs/transformations|Transformations Documentation} + * [Transformations Documentation](https://imagekit.io/docs/transformations) */ export interface Transformation { /** * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.4` represents 40% of the original width). * You can also supply arithmetic expressions (e.g., `iw_div_2`). * - * Width transformation - {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#width---w|Videos} + * Width transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) | [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) */ width?: number | string; @@ -22,7 +22,7 @@ export interface Transformation { * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.5` represents 50% of the original height). * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). * - * Height transformation - {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#height---h|Videos} + * Height transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) | [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) */ height?: number | string; @@ -30,7 +30,7 @@ export interface Transformation { * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. * - * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar|Image Resize and Crop - Aspect Ratio} + * [Image Resize and Crop - Aspect Ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar) */ aspectRatio?: number | string; @@ -38,16 +38,16 @@ export interface Transformation { * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background|Effects and Enhancements - Solid Color Background} + * [Effects and Enhancements - Solid Color Background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background) * * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. * - * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background|Effects and Enhancements - Blurred Background} + * [Effects and Enhancements - Blurred Background](https://imagekit.io/docs/effects-and-enhancements#blurred-background) * * - Expand the image boundaries using generative fill: `genfill`. Not supported inside overlay. Optionally, control the background scene by passing a text prompt: * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. * - * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill|AI Transformations - Generative Fill Background} + * [AI Transformations - Generative Fill Background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill) */ background?: string; @@ -55,31 +55,31 @@ export interface Transformation { * Adds a border to the output media. Accepts a string in the format `_` * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#border---b|Effects and Enhancements - Border} + * [Effects and Enhancements - Border](https://imagekit.io/docs/effects-and-enhancements#border---b) */ border?: string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} + * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) */ crop?: "force" | "at_max" | "at_max_enlarge" | "at_least" | "maintain_ratio"; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} + * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) */ cropMode?: "pad_resize" | "extract" | "pad_extract"; /** * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio (DPR) calculation. * - * {@link https://imagekit.io/docs/image-resize-and-crop#dpr---dpr|Image Resize and Crop - DPR} + * [Image Resize and Crop - DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr) */ dpr?: number /** * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. * - * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo|Image Resize and Crop - Focus} + * [Image Resize and Crop - Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo) */ focus?: string; @@ -87,27 +87,27 @@ export interface Transformation { * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. * - * {@link https://imagekit.io/docs/image-optimization#quality---q|Image Optimization - Quality} + * [Image Optimization - Quality](https://imagekit.io/docs/image-optimization#quality---q) */ quality?: number; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} + * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) */ x?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} + * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) */ xCenter?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} + * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) */ y?: number | string; /** - * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} + * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) */ yCenter?: number | string; @@ -116,28 +116,28 @@ export interface Transformation { * You can also pass `orig` for images to return the original format. * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. * - * {@link https://imagekit.io/docs/image-optimization#format---f|Image Optimization - Format} & {@link https://imagekit.io/docs/video-optimization#format---f|Video Optimization - Format} + * [Image Optimization - Format](https://imagekit.io/docs/image-optimization#format---f) & [Video Optimization - Format](https://imagekit.io/docs/video-optimization#format---f) */ format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; /** * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. * - * {@link https://imagekit.io/docs/video-optimization#video-codec---vc|Video Optimization - Video Codec} + * [Video Optimization - Video Codec](https://imagekit.io/docs/video-optimization#video-codec---vc) */ videoCodec?: "h264" | "vp9" | "av1" | "none"; /** * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. * - * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac|Video Optimization - Audio Codec} + * [Video Optimization - Audio Codec](https://imagekit.io/docs/video-optimization#audio-codec---ac) */ audioCodec?: "aac" | "opus" | "none"; /** * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. * - * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r|Effects and Enhancements - Radius} + * [Effects and Enhancements - Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r) */ radius?: number | "max"; @@ -146,26 +146,26 @@ export interface Transformation { * or `auto` to use the orientation specified in the image's EXIF data. * For videos, only the following values are supported: 0, 90, 180, 270, or 360. * - * {@link https://imagekit.io/docs/effects-and-enhancements#rotate---rt|Effects and Enhancements - Rotate} + * [Effects and Enhancements - Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt) */ rotation?: number | string; /** * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl|Effects and Enhancements - Blur} + * [Effects and Enhancements - Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl) */ blur?: number; /** - * {@link https://imagekit.io/docs/transformations#named-transformations|Transformations - Named Transformations} + * [Transformations - Named Transformations](https://imagekit.io/docs/transformations#named-transformations) */ named?: string; /** * Specifies a fallback image if the resource is not found, e.g., a URL or file path. * - * {@link https://imagekit.io/docs/image-transformation#default-image---di|Image Transformation - Default Image} + * [Image Transformation - Default Image](https://imagekit.io/docs/image-transformation#default-image---di) */ defaultImage?: string; @@ -173,14 +173,14 @@ export interface Transformation { * Flips or mirrors an image either horizontally, vertically, or both. * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. * - * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl|Effects and Enhancements - Flip} + * [Effects and Enhancements - Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl) */ flip?: "h" | "v" | "h_v" | "v_h"; /** * If set to true, serves the original file without applying any transformations. * - * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true|Core Delivery Features - Deliver Original File As Is} + * [Core Delivery Features - Deliver Original File As Is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true) */ original?: boolean; @@ -188,7 +188,7 @@ export interface Transformation { * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. * Arithmetic expressions are also supported. * - * {@link https://imagekit.io/docs/trim-videos#start-offset---so|Trim Videos - Start Offset} + * [Trim Videos - Start Offset](https://imagekit.io/docs/trim-videos#start-offset---so) */ startOffset?: number | string; @@ -196,7 +196,7 @@ export interface Transformation { * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. * Typically used with startOffset to define a time window. Arithmetic expressions are supported. * - * {@link https://imagekit.io/docs/trim-videos#end-offset---eo|Trim Videos - End Offset} + * [Trim Videos - End Offset](https://imagekit.io/docs/trim-videos#end-offset---eo) */ endOffset?: number | string; @@ -204,35 +204,35 @@ export interface Transformation { * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. * - * {@link https://imagekit.io/docs/trim-videos#duration---du|Trim Videos - Duration} + * [Trim Videos - Duration](https://imagekit.io/docs/trim-videos#duration---du) */ duration?: number | string; /** * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. * - * {@link https://imagekit.io/docs/adaptive-bitrate-streaming|Adaptive Bitrate Streaming} + * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) */ streamingResolutions?: StreamingResolution[]; /** * Enables a grayscale effect for images. * - * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale|Effects and Enhancements - Grayscale} + * [Effects and Enhancements - Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale) */ grayscale?: true; /** * Upscales images beyond their original dimensions using AI. Not supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} + * [AI Transformations - Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale) */ aiUpscale?: true /** * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} + * [AI Transformations - Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch) */ aiRetouch?: true @@ -240,7 +240,7 @@ export interface Transformation { * Generates a variation of an image using AI. This produces a new image with slight variations from the original, * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} + * [AI Transformations - Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar) */ aiVariation?: true @@ -250,7 +250,7 @@ export interface Transformation { * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. * Supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow|AI Transformations - Drop Shadow} + * [AI Transformations - Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow) */ aiDropShadow?: true | string @@ -259,7 +259,7 @@ export interface Transformation { * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. * Not supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg|AI Transformations - Change Background} + * [AI Transformations - Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg) */ aiChangeBackground?: string; @@ -267,7 +267,7 @@ export interface Transformation { * Applies ImageKit’s in-house background removal. * Supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} + * [AI Transformations - Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove) */ aiRemoveBackground?: true @@ -276,14 +276,14 @@ export interface Transformation { * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. * Supported inside overlay. * - * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg|AI Transformations - External Background Removal} + * [AI Transformations - External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg) */ aiRemoveBackgroundExternal?: true /** * Automatically enhances the contrast of an image (contrast stretch). * - * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast|Effects and Enhancements - Contrast Stretch} + * [Effects and Enhancements - Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast) */ contrastStretch?: true @@ -292,7 +292,7 @@ export interface Transformation { * For AI-based drop shadows, refer to aiDropShadow. * Pass `true` for a default shadow, or provide a string for a custom shadow. * - * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow|Effects and Enhancements - Shadow} + * [Effects and Enhancements - Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow) */ shadow?: true | string @@ -300,7 +300,7 @@ export interface Transformation { * Sharpens the input image, highlighting edges and finer details. * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. * - * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} + * [Effects and Enhancements - Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen) */ sharpen?: true | number @@ -308,14 +308,14 @@ export interface Transformation { * Applies Unsharp Masking (USM), an image sharpening technique. * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. * - * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} + * [Effects and Enhancements - Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm) */ unsharpMask?: true | string; /** * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. * - * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} + * [Effects and Enhancements - Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient) */ gradient?: true | string; @@ -323,21 +323,21 @@ export interface Transformation { * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, * pixelated version of the full image, which gradually improves to provide a faster perceived load time. * - * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr|Image Optimization - Progressive Image} + * [Image Optimization - Progressive Image](https://imagekit.io/docs/image-optimization#progressive-image---pr) */ progressive?: boolean; /** * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. * - * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo|Image Optimization - Lossless Compression} + * [Image Optimization - Lossless Compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo) */ lossless?: boolean /** * Indicates whether the output image should retain the original color profile. * - * {@link https://imagekit.io/docs/image-optimization#color-profile---cp|Image Optimization - Color Profile} + * [Image Optimization - Color Profile](https://imagekit.io/docs/image-optimization#color-profile---cp) */ colorProfile?: boolean; @@ -345,14 +345,14 @@ export interface Transformation { * By default, ImageKit removes all metadata during automatic image compression. * Set this to true to preserve metadata. * - * {@link https://imagekit.io/docs/image-optimization#image-metadata---md|Image Optimization - Image Metadata} + * [Image Optimization - Image Metadata](https://imagekit.io/docs/image-optimization#image-metadata---md) */ metadata?: boolean; /** * Specifies the opacity level of the output image. * - * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o|Effects and Enhancements - Opacity} + * [Effects and Enhancements - Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o) */ opacity?: number; @@ -360,7 +360,7 @@ export interface Transformation { * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, * leaving only the central object in the output image. * - * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t|Effects and Enhancements - Trim Edges} + * [Effects and Enhancements - Trim Edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t) */ trim?: true | number; @@ -368,7 +368,7 @@ export interface Transformation { * Accepts a numeric value that determines how much to zoom in or out of the cropped area. * It should be used in conjunction with fo-face or fo-. * - * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z|Image Resize and Crop - Zoom} + * [Image Resize and Crop - Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z) */ zoom?: number; @@ -377,7 +377,7 @@ export interface Transformation { * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), * or by name (e.g., `name-layer-4` for a PSD layer). * - * {@link https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files|Vector and Animated Images - Thumbnail Extraction} + * [Vector and Animated Images - Thumbnail Extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files) */ page?: number | string; @@ -392,7 +392,7 @@ export interface Transformation { * Specifies an overlay to be applied on the parent image or video. * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. * - * {@link https://imagekit.io/docs/transformations#overlay-using-layers|Transformations - Overlay Using Layers} + * [Transformations - Overlay Using Layers](https://imagekit.io/docs/transformations#overlay-using-layers) */ overlay?: Overlay; } @@ -409,7 +409,7 @@ export interface BaseOverlay { * Specifies the overlay's position relative to the parent asset. * Accepts a JSON object with `x` and `y` (or `focus`) properties. * - * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} + * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) */ position?: OverlayPosition; @@ -417,7 +417,7 @@ export interface BaseOverlay { * Specifies timing information for the overlay (only applicable if the base asset is a video). * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. * - * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} + * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) */ timing?: OverlayTiming; } @@ -527,7 +527,7 @@ export interface ImageOverlay extends BaseOverlay { /** * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. * - * {@link https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay|Video} + * [Image](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) | [Video](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay) */ transformation?: Transformation[]; } @@ -554,7 +554,7 @@ export interface VideoOverlay extends BaseOverlay { /** * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. * - * {@link https://imagekit.io/docs/video-transformation|Video Transformations} + * [Video Transformations](https://imagekit.io/docs/video-transformation) */ transformation?: Transformation[]; } @@ -581,7 +581,7 @@ export interface SubtitleOverlay extends BaseOverlay { /** * Control styling of the subtitle. * - * {@link https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer|Styling subtitles} + * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ transformation?: SubtitleOverlayTransformation[]; } @@ -597,7 +597,7 @@ export interface SolidColorOverlay extends BaseOverlay { /** * Control width and height of the solid color overlay. Supported transformations depend on the base/parent asset. * - * {@link https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay|Video} + * [Image](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) | [Video](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay) */ transformation?: SolidColorOverlayTransformation[]; } From 064596c43e5382ae95d91dd4a854716b47458e64 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 16:29:01 +0530 Subject: [PATCH 127/166] fix: correct version format in package.json and package-lock.json --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9bf6f47..acb51e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0.beta.1", + "version": "5.0.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0.beta.1", + "version": "5.0.0-beta.1", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 24cc67d..c72cfe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0.beta.1", + "version": "5.0.0-beta.1", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", @@ -72,4 +72,4 @@ "url": "https://github.com/imagekit-developer/imagekit-javascript/issues" }, "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme" -} \ No newline at end of file +} From 27a92f264d94ad3d347d6a274655ac0e591eaa40 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 16:32:44 +0530 Subject: [PATCH 128/166] fix: update version to 5.0.0-beta.2 and adjust publish configuration --- package-lock.json | 4 ++-- package.json | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index acb51e4..217ad1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.1", + "version": "5.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.1", + "version": "5.0.0-beta.2", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index c72cfe5..ab014bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.1", + "version": "5.0.0-beta.2", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", @@ -49,7 +49,8 @@ "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" }, "publishConfig": { - "tag": "beta" + "tag": "beta", + "access": "public" }, "repository": { "type": "git", From b59f621e786f939ef655debd1c1598ad3ae4af71 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Sun, 6 Apr 2025 16:45:09 +0530 Subject: [PATCH 129/166] fix: update version to 5.0.0-beta.3 and enhance npm publish workflow --- .github/workflows/npmpublish.yml | 12 +++++++++++- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 23298d3..44a47a4 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -42,7 +42,17 @@ jobs: npm install npm run build npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN - npm publish + # print the NPM user name for validation + npm whoami + VERSION=$(node -p "require('./package.json').version" ) + # Only publish stable versions to the latest tag + if [[ "$VERSION" =~ ^[^-]+$ ]]; then + NPM_TAG="latest" + else + NPM_TAG="beta" + fi + echo "Publishing $VERSION with $NPM_TAG tag." + npm publish --tag $NPM_TAG --access public env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} CI: true \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 217ad1b..007430a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.2", + "version": "5.0.0-beta.3", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index ab014bc..80ae10f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.2", + "version": "5.0.0-beta.3", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 8d31d57472235ca960413ca6d847965f1938643e Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 10:39:41 +0530 Subject: [PATCH 130/166] test: add assertion for requestId in file upload response --- test/upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/upload.js b/test/upload.js index 5018330..a5c7004 100644 --- a/test/upload.js +++ b/test/upload.js @@ -975,9 +975,9 @@ describe("File upload", async function () { await sleep(); const response = await uploadPromise; - // Make sure your upload.ts preserves the case of "Content-Type" expect(response.$ResponseMetadata.headers).to.deep.equal(dummyResponseHeaders); expect(response.$ResponseMetadata.statusCode).to.equal(200); + expect(response.$ResponseMetadata.requestId).to.equal("sdfsdfsdfdsf"); }); it('Undefined fields should not be sent', async function () { From 42345c3afb50ace9730ccced85fc898214746852 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 11:08:29 +0530 Subject: [PATCH 131/166] refactor: remove unused error messages and delete test data file --- src/constants/errorMessages.ts | 12 +----------- test/data/index.js | 5 ----- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 test/data/index.js diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index b5a3eb1..4b394f9 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -1,24 +1,14 @@ export default { - MANDATORY_INITIALIZATION_MISSING: { message: "Missing urlEndpoint during SDK initialization" }, - INVALID_TRANSFORMATION_POSITION: { message: "Invalid transformationPosition parameter" }, - PRIVATE_KEY_CLIENT_SIDE: { message: "privateKey should not be passed on the client side" }, - MISSING_UPLOAD_DATA: { message: "Missing data for upload" }, MISSING_UPLOAD_FILE_PARAMETER: { message: "Missing file parameter for upload" }, MISSING_UPLOAD_FILENAME_PARAMETER: { message: "Missing fileName parameter for upload" }, - MISSING_AUTHENTICATION_ENDPOINT: { message: "Missing authentication endpoint for upload" }, MISSING_PUBLIC_KEY: { message: "Missing public key for upload" }, - AUTH_ENDPOINT_TIMEOUT: { message: "The authenticationEndpoint you provided timed out in 60 seconds" }, - AUTH_ENDPOINT_NETWORK_ERROR: { message: "Request to authenticationEndpoint failed due to network error" }, - AUTH_INVALID_RESPONSE: { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire." }, UPLOAD_ENDPOINT_NETWORK_ERROR: { message: "Request to ImageKit upload endpoint failed due to network error" }, - INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter" }, MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication." }, MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication." }, MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication." }, INVALID_TRANSFORMATION: { message: "Invalid transformation parameter. Please include at least pre, post, or both." }, INVALID_PRE_TRANSFORMATION: { message: "Invalid pre transformation parameter." }, - INVALID_POST_TRANSFORMATION: { message: "Invalid post transformation parameter." }, - UPLOAD_ABORTED: { message: "Request aborted by the user" }, + INVALID_POST_TRANSFORMATION: { message: "Invalid post transformation parameter." } }; diff --git a/test/data/index.js b/test/data/index.js deleted file mode 100644 index 1ec1645..0000000 --- a/test/data/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports.initializationParams = { - publicKey: "test_public_key", - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - transformationPosition: "path", -} \ No newline at end of file From 6b32005bbcf1ed163b932967905ffdc7fa016cb8 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 14:06:02 +0530 Subject: [PATCH 132/166] test: add unit tests for buildTransformationString function --- .../buildtransformationString.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/url-generation/buildtransformationString.js diff --git a/test/url-generation/buildtransformationString.js b/test/url-generation/buildtransformationString.js new file mode 100644 index 0000000..f116b2e --- /dev/null +++ b/test/url-generation/buildtransformationString.js @@ -0,0 +1,26 @@ +const { buildTransformationString } = require("../../src/index"); +const { expect } = require('chai'); + +describe('buildTransformationString', function () { + it('should return an empty string when no transformations are provided', function () { + const result = buildTransformationString([{}]); + expect(result).to.equal(''); + }); + + it('should generate a transformation string for width only', function () { + const result = buildTransformationString([{ width: 300 }]); + expect(result).to.equal('w-300'); + }); + + it('should generate a transformation string for multiple transformations', function () { + const result = buildTransformationString([ + { + overlay: { + type: 'text', + text: 'Hello', + } + } + ]); + expect(result).to.equal('l-text,i-Hello,l-end'); + }); +}); From 814af9d7f07050dfc32c191830b36285da5ffa0f Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 14:15:38 +0530 Subject: [PATCH 133/166] fix: update version to 5.0.0-beta.4 in package.json and package-lock.json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 007430a..3704588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.2", + "version": "5.0.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.3", + "version": "5.0.0-beta.4", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 80ae10f..8a51d06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.3", + "version": "5.0.0-beta.4", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From c6b9f8054c09a88372d01d4a56a0b2e47ee28443 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 14:32:05 +0530 Subject: [PATCH 134/166] fix: update version to 5.0.0-beta.5 in package.json and package-lock.json, fix types location --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3704588..488b95b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.4", + "version": "5.0.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.4", + "version": "5.0.0-beta.5", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 8a51d06..dd2e5f6 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.4", + "version": "5.0.0-beta.5", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", "browser": "dist/imagekit.min.js", "unpkg": "dist/imagekit.min.js", - "types": "dist/src/index.d.ts", + "types": "dist/index.d.ts", "files": [ "dist", "src" From 0efc78d51e1db8b24d80bb7b5c1323fa2c585f70 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 7 Apr 2025 17:59:03 +0530 Subject: [PATCH 135/166] fix: make encoding property optional in overlay interfaces --- src/interfaces/Transformation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 774773b..b45be9f 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -496,7 +496,7 @@ export interface TextOverlay extends BaseOverlay { * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. */ - encoding: "auto" | "plain" | "base64"; + encoding?: "auto" | "plain" | "base64"; /** * Control styling of the text overlay. @@ -522,7 +522,7 @@ export interface ImageOverlay extends BaseOverlay { * - Leading and trailing slashes are removed. * - Remaining slashes within the path are replaced with `@@` when using plain text. */ - encoding: "auto" | "plain" | "base64"; + encoding?: "auto" | "plain" | "base64"; /** * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. @@ -549,7 +549,7 @@ export interface VideoOverlay extends BaseOverlay { * - Leading and trailing slashes are removed. * - Remaining slashes within the path are replaced with `@@` when using plain text. */ - encoding: "auto" | "plain" | "base64"; + encoding?: "auto" | "plain" | "base64"; /** * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. @@ -576,7 +576,7 @@ export interface SubtitleOverlay extends BaseOverlay { * - Leading and trailing slashes are removed. * - Remaining slashes within the path are replaced with `@@` when using plain text. */ - encoding: "auto" | "plain" | "base64"; + encoding?: "auto" | "plain" | "base64"; /** * Control styling of the subtitle. From 4721a805e636e8df0ab1b9ad62d7c80e8addc176 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 8 Apr 2025 10:01:17 +0530 Subject: [PATCH 136/166] fix: update version to 5.0.0-beta.6 in package.json and package-lock.json, change imports to type imports --- package-lock.json | 4 ++-- package.json | 2 +- src/index.ts | 4 +--- src/upload.ts | 2 +- src/url.ts | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 488b95b..7232e38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index dd2e5f6..4cf3903 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", diff --git a/src/index.ts b/src/index.ts index 95ab767..890c47c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; @@ -9,5 +9,3 @@ export type { UploadOptions, UploadResponse }; - - diff --git a/src/upload.ts b/src/upload.ts index c602894..166ed46 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,5 +1,5 @@ import errorMessages from "./constants/errorMessages"; -import { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; +import type { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; /** * Represents an error when a request to ImageKit is invalid. diff --git a/src/url.ts b/src/url.ts index c256af1..836bb8e 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,5 +1,5 @@ -import { SrcOptions } from "./interfaces"; -import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; +import type { SrcOptions } from "./interfaces"; +import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') From dc4e067ba84413b5ae6b0426d4be95819872c47e Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 8 Apr 2025 10:48:26 +0530 Subject: [PATCH 137/166] Revert "fix: update version to 5.0.0-beta.6 in package.json and package-lock.json, change imports to type imports" This reverts commit 4721a805e636e8df0ab1b9ad62d7c80e8addc176. --- package-lock.json | 4 ++-- package.json | 2 +- src/index.ts | 4 +++- src/upload.ts | 2 +- src/url.ts | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7232e38..488b95b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.5", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 4cf3903..dd2e5f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.5", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", diff --git a/src/index.ts b/src/index.ts index 890c47c..95ab767 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; @@ -9,3 +9,5 @@ export type { UploadOptions, UploadResponse }; + + diff --git a/src/upload.ts b/src/upload.ts index 166ed46..c602894 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,5 +1,5 @@ import errorMessages from "./constants/errorMessages"; -import type { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; +import { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; /** * Represents an error when a request to ImageKit is invalid. diff --git a/src/url.ts b/src/url.ts index 836bb8e..c256af1 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,5 +1,5 @@ -import type { SrcOptions } from "./interfaces"; -import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; +import { SrcOptions } from "./interfaces"; +import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') From 32c07ceabd3b62dad1a0e86583bdd01aeb714d5d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 8 Apr 2025 10:49:21 +0530 Subject: [PATCH 138/166] fix: update version to 5.0.0-beta.6 in package.json and package-lock.json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 488b95b..7232e38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index dd2e5f6..4cf3903 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.5", + "version": "5.0.0-beta.6", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From e6308cb505aadb3a496593da72dd5fc68dfbb4ac Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Tue, 8 Apr 2025 14:43:42 +0530 Subject: [PATCH 139/166] fix: replace formData.append with formData.set for upload options --- package-lock.json | 4 ++-- package.json | 2 +- src/upload.ts | 24 +++++++++++------------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7232e38..890e8c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.7", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 4cf3903..621e2b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.7", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", diff --git a/src/upload.ts b/src/upload.ts index c602894..330966a 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -127,36 +127,34 @@ export const upload = (uploadOptions: UploadOptions): Promise => for (key in uploadOptions) { if (key) { if (key === "file" && typeof uploadOptions.file != "string") { - formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); + formData.set('file', uploadOptions.file, String(uploadOptions.fileName)); } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { - formData.append('tags', uploadOptions.tags.join(",")); + formData.set('tags', uploadOptions.tags.join(",")); } else if (key === 'signature') { - formData.append("signature", uploadOptions.signature); + formData.set("signature", uploadOptions.signature); } else if (key === 'expire') { - formData.append("expire", String(uploadOptions.expire)); + formData.set("expire", String(uploadOptions.expire)); } else if (key === 'token') { - formData.append("token", uploadOptions.token); + formData.set("token", uploadOptions.token); } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { - formData.append('responseFields', uploadOptions.responseFields.join(",")); + formData.set('responseFields', uploadOptions.responseFields.join(",")); } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { - formData.append('extensions', JSON.stringify(uploadOptions.extensions)); + formData.set('extensions', JSON.stringify(uploadOptions.extensions)); } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { - formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); + formData.set('customMetadata', JSON.stringify(uploadOptions.customMetadata)); } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && uploadOptions.transformation !== null) { - formData.append(key, JSON.stringify(uploadOptions.transformation)); + formData.set(key, JSON.stringify(uploadOptions.transformation)); } else if (key === 'checks' && uploadOptions.checks) { - formData.append("checks", uploadOptions.checks); + formData.set("checks", uploadOptions.checks); } else if (uploadOptions[key] !== undefined) { if (["onProgress", "abortSignal"].includes(key)) continue; - formData.append(key, String(uploadOptions[key])); + formData.set(key, String(uploadOptions[key])); } } } - formData.append("publicKey", uploadOptions.publicKey); - if (uploadOptions.onProgress) { xhr.upload.onprogress = function (event: ProgressEvent) { if (uploadOptions.onProgress) uploadOptions.onProgress(event) From 63ebc6d259e12037d72630ea7b23195a900abc17 Mon Sep 17 00:00:00 2001 From: "ImageKit.io" <45416977+imagekitio@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:04:39 +0530 Subject: [PATCH 140/166] Update src/interfaces/UploadOptions.ts Co-authored-by: Abhinav Dhiman <8640877+ahnv@users.noreply.github.com> --- src/interfaces/UploadOptions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 7c4a2dd..0c86491 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -30,6 +30,8 @@ interface Transformation { * - protocol: Used only when type is "abs". Can be "hls" or "dash". * * Refer to the docs for more details on transformations and usage in post. + * + * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#post-transformation} */ post?: PostTransformation[]; } From d8a2d11cd1ddb553bc49990743a4a8682cac423e Mon Sep 17 00:00:00 2001 From: "ImageKit.io" <45416977+imagekitio@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:04:48 +0530 Subject: [PATCH 141/166] Update src/interfaces/UploadOptions.ts Co-authored-by: Abhinav Dhiman <8640877+ahnv@users.noreply.github.com> --- src/interfaces/UploadOptions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 0c86491..1eaed4b 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -20,6 +20,8 @@ interface Transformation { /** * Specifies pre-transformations to be applied. Must be a valid string of transformations like "w-300,h-300". * Refer to the docs for more details on transformations. + * + * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#pre-transformation} */ pre?: string; From d3e57186f170f22cf21e6fcc9a8f27e8797b26e9 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 10 Apr 2025 16:53:15 +0530 Subject: [PATCH 142/166] fix: change imports to type imports for better type checking --- src/index.ts | 2 +- src/upload.ts | 2 +- src/url.ts | 4 ++-- src/utils/transformation.ts | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 95ab767..4b047e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; diff --git a/src/upload.ts b/src/upload.ts index 330966a..e5a8fea 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,5 +1,5 @@ import errorMessages from "./constants/errorMessages"; -import { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; +import type { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; /** * Represents an error when a request to ImageKit is invalid. diff --git a/src/url.ts b/src/url.ts index c256af1..836bb8e 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,5 +1,5 @@ -import { SrcOptions } from "./interfaces"; -import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; +import type { SrcOptions } from "./interfaces"; +import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts index 9578f74..324fef0 100644 --- a/src/utils/transformation.ts +++ b/src/utils/transformation.ts @@ -3,8 +3,6 @@ import { TransformationPosition, SrcOptions } from "../interfaces"; const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; const PATH_TRANSFORMATION_POSITION: TransformationPosition = "path"; -const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = QUERY_TRANSFORMATION_POSITION; -const VALID_TRANSFORMATION_POSITIONS = [PATH_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; const CHAIN_TRANSFORM_DELIMITER: string = ":"; const TRANSFORM_DELIMITER: string = ","; const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; From b76ba5e74b432d05a4df672b1cc9e228c1c28214 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:12:11 +0530 Subject: [PATCH 143/166] fix: update version to 5.0.0 in package.json and package-lock.json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 890e8c1..aa14b58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.7", + "version": "5.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0-beta.7", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 621e2b3..c1b3436 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0-beta.7", + "version": "5.0.0", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 3153f03fa3037c1f3f30341f1c7452e1c1ec0f28 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:13:11 +0530 Subject: [PATCH 144/166] fix: remove src from files array in package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index c1b3436..b64f91a 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "unpkg": "dist/imagekit.min.js", "types": "dist/index.d.ts", "files": [ - "dist", - "src" + "dist" ], "devDependencies": { "@babel/cli": "^7.10.5", From 33fd9ccb6ed7f86d55d60a606cf70a56ac0b7695 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:24:56 +0530 Subject: [PATCH 145/166] chore: update CHANGELOG for version 5.0.0 with breaking changes and new features --- CHANGELOG.md | 24 + diff.diff | 6713 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 6737 insertions(+) create mode 100644 diff.diff diff --git a/CHANGELOG.md b/CHANGELOG.md index d98f048..220aee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## Version 5.0.0 + +This version introduces major breaking changes, for usage examples, refer to the [official documentation](https://imagekit.io/docs/integration/javascript). + +1. The package has been renamed from `imagekit-javascript` to `@imagekit/javascript`. + Please update your dependency references and import statements accordingly. + +2. The default-exported `ImageKit` class has been removed and replaced with named exports: + - `buildSrc` + - `buildTransformationString` + - `upload` + - Error classes: `ImageKitInvalidRequestError`, `ImageKitAbortError`, `ImageKitUploadNetworkError`, `ImageKitServerError` + + Update your imports to use these named exports. + +3. The `upload` method supports `AbortSignal` for canceling uploads. + Upload error handling has been revamped, and `onProgress` callbacks are now supported. + Only the Promise-based syntax is supported. Callback style usage has been removed. + +4. Several internal interfaces (e.g., `ImageKitOptions`, `IKResponse`) have been updated or removed. + The upload options now require a `publicKey`, and a new `SrcOptions` interface has been introduced. + +--- + ## Version 4.0.1 ### Bug Fixes diff --git a/diff.diff b/diff.diff new file mode 100644 index 0000000..2a56085 --- /dev/null +++ b/diff.diff @@ -0,0 +1,6713 @@ +diff --git a/.babelrc b/.babelrc +index e1f3fc1..081c354 100644 +--- a/.babelrc ++++ b/.babelrc +@@ -1,5 +1,8 @@ + { +- "plugins": ["@babel/plugin-proposal-class-properties"], ++ "plugins": [ ++ ["@babel/plugin-transform-class-properties", { "loose": true }], ++ "@babel/plugin-transform-optional-chaining" ++], + "presets": [ + "@babel/preset-typescript", + [ +diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml +index b328240..102ba51 100644 +--- a/.github/workflows/nodejs.yml ++++ b/.github/workflows/nodejs.yml +@@ -13,7 +13,7 @@ jobs: + + strategy: + matrix: +- node-version: [12.x] ++ node-version: [20.x] + + steps: + - uses: actions/checkout@v1 +@@ -26,6 +26,7 @@ jobs: + npm install + npm run build + npm run test +- npm run report-coverage + env: + CI: true ++ - name: Upload coverage to Codecov ++ uses: codecov/codecov-action@v3 +diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml +index 0159c40..44a47a4 100644 +--- a/.github/workflows/npmpublish.yml ++++ b/.github/workflows/npmpublish.yml +@@ -12,7 +12,7 @@ jobs: + + strategy: + matrix: +- node-version: [12.x] ++ node-version: [20.x] + + steps: + - uses: actions/checkout@v1 +@@ -35,14 +35,24 @@ jobs: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: +- node-version: 12 ++ node-version: 20 + registry-url: https://registry.npmjs.org/ + - name: NPM Publish + run: | + npm install + npm run build + npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN +- npm publish ++ # print the NPM user name for validation ++ npm whoami ++ VERSION=$(node -p "require('./package.json').version" ) ++ # Only publish stable versions to the latest tag ++ if [[ "$VERSION" =~ ^[^-]+$ ]]; then ++ NPM_TAG="latest" ++ else ++ NPM_TAG="beta" ++ fi ++ echo "Publishing $VERSION with $NPM_TAG tag." ++ npm publish --tag $NPM_TAG --access public + env: + NODE_AUTH_TOKEN: ${{secrets.npm_token}} + CI: true +\ No newline at end of file +diff --git a/.gitignore b/.gitignore +index 48d5826..9361b82 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -7,4 +7,5 @@ dist + .nyc_output + coverage.lcov + coverage +-out-tsc +\ No newline at end of file ++out-tsc ++docs +\ No newline at end of file +diff --git a/.mocharc.json b/.mocharc.json +index b758d0a..b06f6a6 100644 +--- a/.mocharc.json ++++ b/.mocharc.json +@@ -1,6 +1,6 @@ + { + "coverage": true, +- "require": ["esm", "./babel-register.js"], ++ "require": ["./babel-register.js"], + "exit": true, + "timeout": "40000" +-} +\ No newline at end of file ++} +diff --git a/.npmignore b/.npmignore +new file mode 100644 +index 0000000..483a9c4 +--- /dev/null ++++ b/.npmignore +@@ -0,0 +1 @@ ++package-lock.json +\ No newline at end of file +diff --git a/README.md b/README.md +index d522861..dfe276c 100644 +--- a/README.md ++++ b/README.md +@@ -2,540 +2,37 @@ + + # ImageKit.io JavaScript SDK + +-![gzip size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=gzip&label=gzip) +-![brotli size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=brotli&label=brotli) ++![gzip size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=gzip&label=gzip) ++![brotli size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=brotli&label=brotli) + ![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) +-[![npm version](https://img.shields.io/npm/v/imagekit-javascript)](https://www.npmjs.com/package/imagekit-javascript) ++[![npm version](https://img.shields.io/npm/v/@imagekit/javascript)](https://www.npmjs.com/package/@imagekit/javascript) + [![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) + [![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) + +-A lightweight JavaScript SDK for generating image and video URLs with transformations, and for uploading files directly from the browser to ImageKit. This SDK is intended for use in the browser only. For Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). ++This lightweight, dependency-free JavaScript SDK is designed specifically for browser use. It provides utility functions to generate image and video `src` URLs using [ImageKit transformations](https://imagekit.io/docs/transformations) and to upload files to the ImageKit media library. + +-## Table of Contents +-- [Installation](#installation) +-- [Initialization](#initialization) +-- [URL Generation](#url-generation) +- - [Basic URL Generation](#basic-url-generation) +- - [Advanced URL Generation Examples](#advanced-url-generation-examples) +- - [Chained Transformations](#chained-transformations) +- - [Overlays and Effects](#overlays-and-effects) +- - [AI and Advanced Transformations](#ai-and-advanced-transformations) +- - [Arithmetic Expressions in Transformations](#arithmetic-expressions-in-transformations) +- - [Supported Transformations](#supported-transformations) +- - [Handling Unsupported Transformations](#handling-unsupported-transformations) +-- [File Upload](#file-upload) +- - [Basic Upload Example](#basic-upload-example) +- - [Promise-based Upload Example](#promise-based-upload-example) +-- [Test Examples](#test-examples) +-- [Changelog](#changelog) ++For server-side applications with Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). + + ## Installation + +-### Using npm +-Install the SDK via npm: +-```bash +-npm install imagekit-javascript --save +-# or +-yarn add imagekit-javascript +-``` +- +-Then import ImageKit: +-```js +-import ImageKit from "imagekit-javascript"; +-// or with CommonJS: +-const ImageKit = require("imagekit-javascript"); +-``` +- +-### Using CDN +-You can also use the global CDN: +- +-Download a specific version: +-``` +-https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js +-``` +-Or for the latest version, remove the version number (don't use in production as it may break your code if a new major version is released): +-``` +-https://unpkg.com/imagekit-javascript/dist/imagekit.min.js +-``` +- +-And include it in your HTML: +-```html +- +-``` +- +-## Initialization +-To use the SDK, initialize it with your ImageKit URL endpoint. You can get the URL endpoint [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from the [developer section](https://imagekit.io/dashboard/developer/api-keys): +- +-```js +-var imagekit = new ImageKit({ +- urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", // Required +- transformationPosition: "query", // Optional, defaults to "query" +- publicKey: "your_public_api_key", // Optional, required only for client-side file uploads +-}); +-``` +- +-> Note: Never include your private API key in client-side code. The SDK will throw an error if you do. +- +-### Initialization Options +- +-| Option | Description | Example | +-| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | +-| urlEndpoint | Required. Your ImageKit URL endpoint or custom domain. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | +-| transformationPosition | Optional. Specifies whether transformations are added as URL path segments (`path`) or query parameters (`query`). The default is `query`, which allows you to perform wildcard purges and remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | +-| publicKey | Optional. Your public API key for client-side uploads. | `publicKey: "your_public_api_key"` | +- +- +-## URL Generation +- +-The SDK’s `.url()` method enables you to generate optimized image and video URLs with a variety of transformations. +- +-The method accepts an object with the following parameters: +- +-| Option | Description | Example | +-| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | +-| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | +-| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | +-| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. See [supported transformations](#supported-transformations). | `[ { width: 300, height: 400 } ]` | +-| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | +- +-Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings for a specific `.url()` call. +- +-### Basic URL Generation +- +-*A simple height and width transformation:* +- +-```js +-var imageURL = imagekit.url({ +- path: "/default-image.jpg", +- urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", +- transformation: [{ +- height: 300, +- width: 400 +- }] +-}); +-``` +- +-*Result Example:* +-``` +-https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,w-400 +-``` +- +-SDK automatically generates the URL based on the provided parameters. The generated URL includes the base URL, path, and transformation parameters. +- +-### Advanced URL Generation Examples +- +-#### Chained Transformations +-Apply multiple transformations by passing an array: +-```js +-var imageURL = imagekit.url({ +- path: "/default-image.jpg", +- transformation: [{ +- height: 300, +- width: 400 +- }, { +- rotation: 90 +- }], +-}); +-``` +- +-*Result Example:* +-``` +-https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400:rt-90 +-``` +- +-#### Overlays and Effects +-*Text Overlay Example:* +-```js +-var imageURL = imagekit.url({ +- src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", +- transformation: [{ +- width: 400, +- height: 300, +- overlay: { +- text: "Imagekit", +- fontSize: 50, +- color: "red", +- position: { +- x: 10, +- y: 20 +- } +- } +- }] +-}); +-``` +- +-*Image Overlay Example:* +- +-```js +-var imageURL = imagekit.url({ +- src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", +- transformation: [{ +- width: 400, +- height: 300, +- overlay: { +- type: "image", +- input: "logo.png", +- transformation: [{ +- width: 100, +- border: "10_CDDC39" +- }], +- position: { +- focus: "top_left" +- } +- } +- }] +-}); +-``` +- +-*Video Overlay Example:* ++You can install the SDK in your project using npm or yarn. + +-```js +-var videoOverlayURL = imagekit.url({ +- src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", +- transformation: [{ +- overlay: { +- type: "video", +- input: "overlay-video.mp4", +- position: { +- x: "10", +- y: "20" +- }, +- timing: { +- start: 5, +- duration: 10 +- } +- } +- }] +-}); +-``` +- +-*Subtitle Overlay Example:* +- +-```js +-var subtitleOverlayURL = imagekit.url({ +- src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", +- transformation: [{ +- overlay: { +- type: "subtitle", +- input: "subtitle.vtt", +- transformation: [{ +- fontSize: 16, +- fontFamily: "Arial" +- }], +- position: { +- focus: "bottom" +- }, +- timing: { +- start: 0, +- duration: 5 +- } +- } +- }] +-}); +-``` +- +-*Solid Color Overlay Example:* +-```js +-var solidColorOverlayURL = imagekit.url({ +- src: "https://ik.imagekit.io/your_imagekit_id/base-image.jpg", +- transformation: [{ +- overlay: { +- type: "solidColor", +- color: "FF0000", +- transformation: [{ +- width: 100, +- height: 50, +- alpha: 5 +- }], +- position: { x: 20, y: 20 } +- } +- }] +-}); +-``` +- +-##### Overlay Options +- +-ImageKit supports various overlay types, including text, image, video, subtitle, and solid color overlays. Each overlay type has specific configuration options to customize the overlay appearance and behavior. To learn more about how overlays work, refer to the [ImageKit documentation](https://imagekit.io/docs/transformations#overlay-using-layers). +- +-The table below outlines the available overlay configuration options: +- +-| Option | Description | Example | +-| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +-| type | Specifies the type of overlay. Supported values: `text`, `image`, `video`, `subtitle`, `solidColor`. | `type: "text"` | +-| text | (For text overlays) The text content to display. | `text: "ImageKit"` | +-| input | (For image, video, or subtitle overlays) Relative path to the overlay asset. | `input: "logo.png"` or `input: "overlay-video.mp4"` | +-| color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | +-| encoding | Accepted values: `auto`, `plain`, `base64`. [Check this](#encoding-options) for more details. | `encoding: "auto"` | +-| transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | +-| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | +-| timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | +- +-##### Encoding Options +- +-Overlay encoding options define how the overlay input is converted for URL construction. When set to `auto`, the SDK automatically determines whether to use plain text or Base64 encoding based on the input content. +- +-For text overlays: +-- If `auto` is used, the SDK checks the text overlay input: if it is URL-safe, it uses the format `i-{input}` (plain text); otherwise, it applies Base64 encoding with the format `ie-{base64_encoded_input}`. +-- You can force a specific method by setting encoding to `plain` (always use `i-{input}`) or `base64` (always use `ie-{base64}`). +-- Note: In all cases, the text is percent-encoded to ensure URL safety. +- +-For image, video, and subtitle overlays: +-- The input path is processed by removing any leading/trailing slashes and replacing inner slashes with `@@` when `plain` is used. +-- Similarly, if `auto` is used, the SDK determines whether to apply plain text or Base64 encoding based on the characters present. +-- For explicit behavior, use `plain` or `base64` to enforce the desired encoding. +- +-Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or `base64` when a specific encoding method is required. +- +-##### Solid Color Overlay Transformations +- +-| Option | Description | Example | +-| ------ | ---------------------------------------------------------------------------------------------------------------------------------- | --------------- | +-| width | Specifies the width of the solid color overlay block (in pixels or as an arithmetic expression). | `width: 100` | +-| height | Specifies the height of the solid color overlay block (in pixels or as an arithmetic expression). | `height: 50` | +-| radius | Specifies the corner radius of the solid color overlay block or shape. Can be a number or `"max"` for circular/oval shapes. | `radius: "max"` | +-| alpha | Specifies the transparency level of the solid color overlay. Supports integers from 1 (most transparent) to 9 (least transparent). | `alpha: 5` | +- +-##### Text Overlay Transformations +- +-| Option | Description | Example | +-| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | +-| width | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions are supported (e.g., `bw_mul_0.2` or `bh_div_2`). | `width: 400` | +-| fontSize | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | +-| fontFamily | Specifies the font family of the overlaid text. Choose from the supported fonts or provide a custom font. | `fontFamily: "Arial"` | +-| fontColor | Specifies the font color of the overlaid text. Accepts an RGB hex code, an RGBA code, or a standard color name. | `fontColor: "FF0000"` | +-| innerAlignment | Specifies the inner alignment of the text when it doesn’t occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | +-| padding | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | +-| alpha | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | +-| typography | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | +-| background | Specifies the background color of the text overlay. Accepts an RGB hex code, an RGBA code, or a color name. | `background: "red"` | +-| radius | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` for circular/oval shape. | `radius: "max"` | +-| rotation | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | +-| flip | Specifies the flip option for the text overlay. Supported values: `h`, `v`, `h_v`, `v_h`. | `flip: "h"` | +-| lineHeight | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | +- +-##### Subtitle Overlay Transformations +- +-| Option | Description | Example | +-| ----------- | --------------------------------------------------------------------------------------------------------- | ----------------------- | +-| background | Specifies the subtitle background color using a standard color name, RGB color code, or RGBA color code. | `background: "blue"` | +-| fontSize | Sets the font size of subtitle text. | `fontSize: 16` | +-| fontFamily | Sets the font family of subtitle text. | `fontFamily: "Arial"` | +-| color | Specifies the font color of subtitle text using standard color name, RGB, or RGBA color code. | `color: "FF0000"` | +-| typography | Sets the typography style of subtitle text. Supported values: `b`, `i`, `b_i`. | `typography: "b"` | +-| fontOutline | Specifies the font outline for subtitles. Requires an outline width and color separated by an underscore. | `fontOutline: "2_blue"` | +-| fontShadow | Specifies the font shadow for subtitles. Requires shadow color and indent separated by an underscore. | `fontShadow: "blue_2"` | +- +-#### AI and Advanced Transformations +-*Background Removal:* +-```js +-var imageURL = imagekit.url({ +- path: "/sample-image.jpg", +- transformation: [{ +- aiRemoveBackground: true +- }] +-}); +-``` +-*Upscaling:* +-```js +-var upscaledURL = imagekit.url({ +- path: "/sample-image.jpg", +- transformation: [{ +- aiUpscale: true +- }] +-}); +-``` +-*Drop Shadow:* +-```js +-var dropShadowURL = imagekit.url({ +- path: "/sample-image.jpg", +- transformation: [{ +- aiDropShadow: "az-45" +- }] +-}); +-``` +- +-#### Arithmetic Expressions in Transformations +-```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" +- }] +-}); +-``` +- +-### Supported Transformations +- +-The SDK gives a name to each transformation parameter (e.g. `height` maps to `h`, `width` maps to `w`). If the property does not match any of the following supported options, it is added as is. +- +-If you want to generate transformations without any modifications, use the `raw` parameter. +- +-Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. +- +-| Transformation Name | URL Parameter | +-| -------------------------- | ---------------------------------------------------------------------------------------------- | +-| width | w | +-| height | h | +-| aspectRatio | ar | +-| quality | q | +-| aiRemoveBackground | e-bgremove (ImageKit powered) | +-| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | +-| aiUpscale | e-upscale | +-| aiRetouch | e-retouch | +-| aiVariation | e-genvar | +-| aiDropShadow | e-dropshadow | +-| aiChangeBackground | e-changebg | +-| crop | c | +-| cropMode | cm | +-| x | x | +-| y | y | +-| xCenter | xc | +-| yCenter | yc | +-| focus | fo | +-| format | f | +-| radius | r | +-| background | bg | +-| border | b | +-| rotation | rt | +-| blur | bl | +-| named | n | +-| dpr | dpr | +-| progressive | pr | +-| lossless | lo | +-| trim | t | +-| metadata | md | +-| colorProfile | cp | +-| defaultImage | di | +-| original | orig | +-| videoCodec | vc | +-| audioCodec | ac | +-| grayscale | e-grayscale | +-| contrastStretch | e-contrast | +-| shadow | e-shadow | +-| sharpen | e-sharpen | +-| unsharpMask | e-usm | +-| gradient | e-gradient | +-| flip | fl | +-| opacity | o | +-| zoom | z | +-| page | pg | +-| startOffset | so | +-| endOffset | eo | +-| duration | du | +-| streamingResolutions | sr | +-| overlay | Generates the correct layer syntax for image, video, text, subtitle, and solid color overlays. | +-| raw | The string provided in raw will be added in the URL as is. | +- +-### Handling Unsupported Transformations +- +-If you specify a transformation parameter that is not explicitly supported by the SDK, it is added “as-is” in the generated URL. This provides flexibility for using new or custom transformations without waiting for an SDK update. +- +-For example: +-```js +-var imageURL = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- newparam: "cool" +- }] +-}); +-// Generated URL: https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=newparam-cool ++```bash ++npm install @imagekit/javascript + ``` + +-## File Upload +- +-The SDK offers a simple interface via the `.upload()` method to upload files to the ImageKit Media Library. This method requires the following: +-- **file** (mandatory) +-- **fileName** (mandatory) +-- Security parameters: **signature**, **token**, and **expire** +- +-Before invoking the upload, generate the necessary security parameters as per the [ImageKit Upload API documentation](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). ++## TypeScript support + +-### Upload Options +-| Option | Description | Example | +-| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | +-| file | The file content to be uploaded. Accepts binary, base64 string, or URL. | `file: fileInput.files[0]` | +-| fileName | The name to assign to the uploaded file. Supports alphanumeric characters, dot, underscore, and dash. | `fileName: "myImage.jpg"` | +-| signature | HMAC-SHA1 digest computed using the private API key. Must be calculated on the server side. | `signature: "generated_signature"` | +-| token | A unique token to prevent duplicate upload retries. Typically a V4 UUID or similar unique string. | `token: "unique_upload_token"` | +-| expire | Unix timestamp (in seconds) indicating the signature expiry time (should be within 1 hour). | `expire: 1616161616` | +-| useUniqueFileName | Boolean flag to automatically generate a unique filename if set to true. Defaults to true. | `useUniqueFileName: true` | +-| folder | The folder path where the file will be uploaded. Automatically creates nested folders if they don’t exist. | `folder: "/images/uploads"` | +-| isPrivateFile | Boolean to mark the file as private, restricting access to the original file URL. Defaults to false. | `isPrivateFile: false` | +-| tags | Tags to associate with the file. Can be a comma-separated string or an array of tags. | `tags: "summer,holiday"` or `tags: ["summer","holiday"]` | +-| customCoordinates | Specifies an area of interest in the image formatted as `x,y,width,height`. | `customCoordinates: "10,10,100,100"` | +-| responseFields | Comma-separated list of fields to include in the upload response. | `responseFields: "tags,customCoordinates"` | +-| extensions | Array of extension objects for additional image processing. | `extensions: [{ name: "auto-tagging" }]` | +-| webhookUrl | URL to which the final status of extension processing will be sent. | `webhookUrl: "https://example.com/webhook"` | +-| overwriteFile | Boolean flag indicating whether to overwrite a file if it exists. Defaults to true. | `overwriteFile: true` | +-| overwriteAITags | Boolean flag to remove AITags from a file if overwritten. Defaults to true. | `overwriteAITags: true` | +-| overwriteTags | Boolean flag that determines if existing tags should be removed when new tags are not provided. Defaults to true when file is overwritten without tags. | `overwriteTags: true` | +-| overwriteCustomMetadata | Boolean flag dictating if existing custom metadata should be removed when not provided. Defaults to true under similar conditions as tags. | `overwriteCustomMetadata: true` | +-| customMetadata | Stringified JSON or an object containing custom metadata key-value pairs to associate with the file. | `customMetadata: {author: "John Doe"}` | +-| transformation | Optional transformation object to apply during the upload process. It follows the same structure as in URL generation. | `transformation: { pre: "w-200,h-200", post: [...] }` | +-| xhr | An optional XMLHttpRequest object provided to monitor upload progress. | `xhr: new XMLHttpRequest()` | +-| checks | Optional string value for specifying server-side checks to run before file upload. | `checks: "file.size' < '1MB'"` | ++The SDK is written in TypeScript, offering first-class TypeScript support. Enjoy excellent type safety and IntelliSense in your IDE. You can use it in your TypeScript projects without any additional configuration. + +-### Basic Upload Example + +-Below is an HTML form example that uses a callback for handling the upload response: +- +-```html +-
+- +- +-
+- +- +-``` +- +-### Promise-based Upload Example +- +-You can also use promises for a cleaner asynchronous approach: +-```js +-imagekit.upload({ +- file: file.files[0], +- fileName: "abc1.jpg", +- token: 'generated_token', +- signature: 'generated_signature', +- expire: 'generated_expire' +-}).then(result => { +- console.log(result); +-}).catch(error => { +- console.error(error); +-}); +-``` ++To enable type checking in JavaScript projects, add `//@ts-check` at the top of your JavaScript files. This will activate type checking in your IDE. + +-## Test Examples ++## Documentation + +-For a quick demonstration of the SDK features, check the test suite: +-- URL generation examples can be found in [basic.js](./test/url-generation/basic.js) and [overlay.js](./test/url-generation/overlay.js) files. +-- File upload examples can be found in [test/upload.js](./test/upload.js). ++Refer to the ImageKit [official documentation](https://imagekit.io/docs/integration/javascript) for more details on how to use the SDK. + + ## Changelog + +-For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). +\ No newline at end of file ++For a detailed history of changes, refer to [CHANGELOG.md](CHANGELOG.md). + +diff --git a/package.json b/package.json +index 2d9a978..b64f91a 100644 +--- a/package.json ++++ b/package.json +@@ -1,19 +1,21 @@ + { +- "name": "imagekit-javascript", +- "version": "4.0.1", +- "description": "Javascript SDK for using ImageKit.io in the browser", ++ "name": "@imagekit/javascript", ++ "version": "5.0.0", ++ "description": "ImageKit Javascript SDK", + "main": "dist/imagekit.cjs.js", + "module": "dist/imagekit.esm.js", + "browser": "dist/imagekit.min.js", + "unpkg": "dist/imagekit.min.js", +- "types": "dist/src/index.d.ts", ++ "types": "dist/index.d.ts", + "files": [ +- "dist", +- "src" ++ "dist" + ], + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.10.5", ++ "@babel/plugin-proposal-class-properties": "^7.18.6", ++ "@babel/plugin-proposal-optional-chaining": "^7.21.0", ++ "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/preset-env": "^7.10.4", + "@babel/preset-typescript": "^7.13.0", + "@babel/register": "^7.14.5", +@@ -24,8 +26,7 @@ + "@types/node": "^15.6.1", + "babel-plugin-transform-class-properties": "^6.24.1", + "chai": "^4.2.0", +- "codecov": "^3.8.0", +- "esm": "^3.2.25", ++ "codecov": "^3.8.3", + "formdata-node": "2.1.0", + "mocha": "^7.0.1", + "nyc": "^15.1.0", +@@ -43,9 +44,12 @@ + "dev": "rollup -c -w", + "export-types": "tsc", + "build": "rm -rf dist*;rollup -c && yarn export-types", +- "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha \"test/**/*.js\"", +- "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", +- "report-coverage": "codecov" ++ "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", ++ "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" ++ }, ++ "publishConfig": { ++ "tag": "beta", ++ "access": "public" + }, + "repository": { + "type": "git", +@@ -54,14 +58,12 @@ + "keywords": [ + "imagekit", + "javascript", +- "sdk", +- "js", + "image", ++ "video", ++ "upload", + "optimization", + "transformation", + "resize", +- "upload", +- "video", + "overlay" + ], + "author": "ImageKit Developer", +@@ -69,6 +71,5 @@ + "bugs": { + "url": "https://github.com/imagekit-developer/imagekit-javascript/issues" + }, +- "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme", +- "dependencies": {} ++ "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme" + } +diff --git a/rollup.config.js b/rollup.config.js +index 747e961..95ae3de 100644 +--- a/rollup.config.js ++++ b/rollup.config.js +@@ -12,8 +12,7 @@ export default [ + output: { + name: "ImageKit", + file: pkg.browser, +- format: "umd", +- sourceMap: true, ++ format: "umd" + }, + plugins: [ + nodeResolve({ extensions: [".ts"] }), +@@ -34,8 +33,8 @@ export default [ + { + input: "src/index.ts", + output: [ +- { file: pkg.main, format: "cjs", exports: "default" }, +- { file: pkg.module, format: "es", exports: "default" }, ++ { file: pkg.main, format: "cjs", exports: "named" }, ++ { file: pkg.module, format: "es", exports: "named" }, + ], + plugins: [ + nodeResolve({ extensions: [".ts"] }), +diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug +index 91d398f..4c53a49 100644 +--- a/samples/sample-app/views/index.pug ++++ b/samples/sample-app/views/index.pug +@@ -23,6 +23,7 @@ html + script(type='text/javascript' src="./imagekit.min.js") + script. + try { ++ window.controller = new AbortController(); + var imagekit = new ImageKit({ + publicKey: "!{publicKey}", + urlEndpoint: "!{urlEndpoint}", +@@ -51,9 +52,11 @@ html + var statusEl = document.getElementById("status"); + statusEl.innerHTML = "Uploading..."; + ++ + // Use this if you want to track upload progress + var customXHR = new XMLHttpRequest(); + customXHR.upload.addEventListener('progress', function (e) { ++ console.log("On progress event handler from customXHR"); + if (e.loaded <= fileSize) { + var percent = Math.round(e.loaded / fileSize * 100); + console.log(`Uploaded ${percent}%`); +@@ -94,6 +97,11 @@ html + token: securityParametersObj.token, + signature: securityParametersObj.signature, + expire: securityParametersObj.expire, ++ signal: window.controller.signal, ++ onProgress: function(e) { ++ console.log("On progress event handler from SDK"); ++ console.log(e.loaded); ++ }, + //- extensions: [ + //- { + //- name: "aws-auto-tagging", +@@ -102,6 +110,7 @@ html + //- } + //- ], + }, function(err, result) { ++ debugger; + if (err) { + statusEl.innerHTML = "Error uploading image. "+ err.message; + console.log(err) +diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts +index 06d8b51..4b394f9 100644 +--- a/src/constants/errorMessages.ts ++++ b/src/constants/errorMessages.ts +@@ -1,24 +1,14 @@ + export default { +- MANDATORY_INITIALIZATION_MISSING: { message: "Missing urlEndpoint during SDK initialization", help: "" }, +- INVALID_TRANSFORMATION_POSITION: { message: "Invalid transformationPosition parameter", help: "" }, +- PRIVATE_KEY_CLIENT_SIDE: { message: "privateKey should not be passed on the client side", help: "" }, +- 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: "" }, +- MISSING_AUTHENTICATION_ENDPOINT: { message: "Missing authentication endpoint for upload", help: "" }, +- MISSING_PUBLIC_KEY: { message: "Missing public key for upload", help: "" }, +- AUTH_ENDPOINT_TIMEOUT: { message: "The authenticationEndpoint you provided timed out in 60 seconds", help: "" }, +- AUTH_ENDPOINT_NETWORK_ERROR: { message: "Request to authenticationEndpoint failed due to network error", help: "" }, +- AUTH_INVALID_RESPONSE: { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, ++ MISSING_UPLOAD_FILE_PARAMETER: { message: "Missing file parameter for upload" }, ++ MISSING_UPLOAD_FILENAME_PARAMETER: { message: "Missing fileName parameter for upload" }, ++ MISSING_PUBLIC_KEY: { message: "Missing public key for upload" }, + UPLOAD_ENDPOINT_NETWORK_ERROR: { +- message: "Request to ImageKit upload endpoint failed due to network error", +- help: "", ++ message: "Request to ImageKit upload endpoint failed due to network error" + }, +- INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, +- MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: ""}, +- MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: ""}, +- MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: ""}, +- 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: ""}, ++ MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication." }, ++ MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication." }, ++ MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication." }, ++ INVALID_TRANSFORMATION: { message: "Invalid transformation parameter. Please include at least pre, post, or both." }, ++ INVALID_PRE_TRANSFORMATION: { message: "Invalid pre transformation parameter." }, ++ INVALID_POST_TRANSFORMATION: { message: "Invalid post transformation parameter." } + }; +diff --git a/src/index.ts b/src/index.ts +index 6594fa7..4b047e1 100644 +--- a/src/index.ts ++++ b/src/index.ts +@@ -1,91 +1,13 @@ +-import { version } from "../package.json"; +-import errorMessages from "./constants/errorMessages"; +-import { ImageKitOptions, UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; +-import IKResponse from "./interfaces/IKResponse"; +-import { upload } from "./upload/index"; +-import respond from "./utils/respond"; +-import { url } from "./url/index"; +-import transformationUtils from "./utils/transformation"; +- +-function mandatoryParametersAvailable(options: ImageKitOptions) { +- return options.urlEndpoint; +-} +- +-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); +- }); +- } +- }; ++import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; ++import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; ++import { buildSrc, buildTransformationString } from "./url"; ++ ++export { buildSrc, buildTransformationString, upload, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; ++export type { ++ Transformation, ++ SrcOptions, ++ UploadOptions, ++ UploadResponse + }; + +-class ImageKit { +- options: ImageKitOptions = { +- publicKey: "", +- urlEndpoint: "", +- transformationPosition: transformationUtils.getDefault(), +- }; +- +- constructor(opts: ImageKitOptions) { +- this.options = { ...this.options, ...(opts || {}) }; +- if (!mandatoryParametersAvailable(this.options)) { +- throw errorMessages.MANDATORY_INITIALIZATION_MISSING; +- } +- +- if (!transformationUtils.validParameters(this.options)) { +- throw errorMessages.INVALID_TRANSFORMATION_POSITION; +- } +- } +- +- /** +- * A utility function to generate asset URL. It applies the specified transformations and other parameters to the URL. +- */ +- url(urlOptions: UrlOptions): string { +- return url(urlOptions, this.options); +- } +- +- /** +- * For uploading files directly from the browser to ImageKit.io. +- * +- * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload} +- */ +- upload(uploadOptions: UploadOptions, options?: Partial): Promise> +- upload(uploadOptions: UploadOptions, callback: (err: Error | null, response: IKResponse | null) => void, options?: Partial): void; +- upload(uploadOptions: UploadOptions, callbackOrOptions?: ((err: Error | null, response: IKResponse | null) => void) | Partial, options?: Partial): void | Promise> { +- let callback; +- if (typeof callbackOrOptions === 'function') { +- callback = callbackOrOptions; +- } else { +- options = callbackOrOptions || {}; +- } +- if (!uploadOptions || typeof uploadOptions !== "object") { +- return respond(true, errorMessages.INVALID_UPLOAD_OPTIONS, callback); +- } +- var mergedOptions = { +- ...this.options, +- ...options, +- }; +- const { xhr: userProvidedXHR } = uploadOptions || {}; +- delete uploadOptions.xhr; +- const xhr = userProvidedXHR || new XMLHttpRequest(); +- return promisify>(this, upload)(xhr, uploadOptions, mergedOptions, callback); +- } +-} + +-export default ImageKit; +diff --git a/src/interfaces/IKResponse.ts b/src/interfaces/IKResponse.ts +deleted file mode 100644 +index a53ca4f..0000000 +--- a/src/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/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts +deleted file mode 100644 +index 6f8b78f..0000000 +--- a/src/interfaces/ImageKitOptions.ts ++++ /dev/null +@@ -1,7 +0,0 @@ +-import { TransformationPosition } from "."; +- +-export interface ImageKitOptions { +- urlEndpoint: string; +- publicKey?: string; +- transformationPosition?: TransformationPosition; +-} +diff --git a/src/interfaces/SrcOptions.ts b/src/interfaces/SrcOptions.ts +new file mode 100644 +index 0000000..4b0187e +--- /dev/null ++++ b/src/interfaces/SrcOptions.ts +@@ -0,0 +1,35 @@ ++import { Transformation } from "./Transformation"; ++import { TransformationPosition } from "."; ++ ++export interface SrcOptions { ++ /** ++ * Accepts a relative or absolute path of the resource. If a relative path is provided, it is appended to the `urlEndpoint`. ++ * If an absolute path is provided, `urlEndpoint` is ignored. ++ */ ++ src: string; ++ ++ /** ++ * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). ++ */ ++ urlEndpoint: string; ++ ++ /** ++ * An array of objects specifying the transformations to be applied in the URL. If more than one transformation is specified, they are applied in the order they are specified as chained transformations. ++ * ++ * {@link https://imagekit.io/docs/transformations#chained-transformations} ++ */ ++ transformation?: Array; ++ ++ /** ++ * These are additional query parameters that you want to add to the final URL. ++ * They can be any query parameters and not necessarily related to ImageKit. ++ * This is especially useful if you want to add a versioning parameter to your URLs. ++ */ ++ queryParameters?: { [key: string]: string | number }; ++ ++ /** ++ * By default, the transformation string is added as a query parameter in the URL, e.g., `?tr=w-100,h-100`. ++ * If you want to add the transformation string in the path of the URL, set this to `path`. ++ */ ++ transformationPosition?: TransformationPosition; ++} +diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts +index adbe8dd..b45be9f 100644 +--- a/src/interfaces/Transformation.ts ++++ b/src/interfaces/Transformation.ts +@@ -4,394 +4,395 @@ export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440 + + /** + * The SDK provides easy-to-use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. +- * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, you can use the `raw` parameter to pass the transformation string directly. ++ * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, ++ * You can use the `raw` parameter to pass the transformation string directly. + * +- * {@link https://imagekit.io/docs/transformations|Transformations Documentation} ++ * [Transformations Documentation](https://imagekit.io/docs/transformations) + */ + export interface Transformation { + /** +- * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage +- * (e.g., `0.4` represents 40% of the original width). You can also supply arithmetic expressions (e.g., `iw_div_2`). ++ * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.4` represents 40% of the original width). ++ * You can also supply arithmetic expressions (e.g., `iw_div_2`). + * +- * Width transformation - {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#width---w|Videos} ++ * Width transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) | [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) + */ + width?: number | string; + + /** +- * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage +- * (e.g., `0.5` represents 50% of the original height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). ++ * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.5` represents 50% of the original height). ++ * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). + * +- * Height transformation - {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#height---h|Videos} ++ * Height transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) | [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) + */ + height?: number | string; + + /** +- * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). ++ * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). + * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. + * +- * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar|Image Resize and Crop - Aspect Ratio} ++ * [Image Resize and Crop - Aspect Ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar) + */ + aspectRatio?: number | string; + + /** +- * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. ++ * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. + * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background|Effects and Enhancements - Solid Color Background} ++ * [Effects and Enhancements - Solid Color Background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background) + * + * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background|Effects and Enhancements - Blurred Background} ++ * [Effects and Enhancements - Blurred Background](https://imagekit.io/docs/effects-and-enhancements#blurred-background) + * + * - Expand the image boundaries using generative fill: `genfill`. Not supported inside overlay. Optionally, control the background scene by passing a text prompt: + * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. + * +- * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill|AI Transformations - Generative Fill Background} ++ * [AI Transformations - Generative Fill Background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill) + */ + background?: string; + + /** +- * Adds a border to the output media. Accepts a string in the format `_` ++ * Adds a border to the output media. Accepts a string in the format `_` + * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#border---b|Effects and Enhancements - Border} ++ * [Effects and Enhancements - Border](https://imagekit.io/docs/effects-and-enhancements#border---b) + */ + border?: string; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} ++ * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) + */ + crop?: "force" | "at_max" | "at_max_enlarge" | "at_least" | "maintain_ratio"; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} ++ * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) + */ + cropMode?: "pad_resize" | "extract" | "pad_extract"; + + /** + * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio (DPR) calculation. + * +- * {@link https://imagekit.io/docs/image-resize-and-crop#dpr---dpr|Image Resize and Crop - DPR} ++ * [Image Resize and Crop - DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr) + */ + dpr?: number + + /** +- * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. ++ * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. + * +- * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo|Image Resize and Crop - Focus} ++ * [Image Resize and Crop - Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo) + */ + focus?: string; + + /** +- * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. ++ * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. + * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. + * +- * {@link https://imagekit.io/docs/image-optimization#quality---q|Image Optimization - Quality} ++ * [Image Optimization - Quality](https://imagekit.io/docs/image-optimization#quality---q) + */ + quality?: number; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} ++ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + */ + x?: number | string; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} ++ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + */ + xCenter?: number | string; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} ++ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + */ + y?: number | string; + + /** +- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} ++ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + */ + yCenter?: number | string; + + /** +- * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. ++ * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. + * You can also pass `orig` for images to return the original format. + * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. + * +- * {@link https://imagekit.io/docs/image-optimization#format---f|Image Optimization - Format} & {@link https://imagekit.io/docs/video-optimization#format---f|Video Optimization - Format} ++ * [Image Optimization - Format](https://imagekit.io/docs/image-optimization#format---f) & [Video Optimization - Format](https://imagekit.io/docs/video-optimization#format---f) + */ + format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; + + /** +- * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. ++ * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. + * +- * {@link https://imagekit.io/docs/video-optimization#video-codec---vc|Video Optimization - Video Codec} ++ * [Video Optimization - Video Codec](https://imagekit.io/docs/video-optimization#video-codec---vc) + */ + videoCodec?: "h264" | "vp9" | "av1" | "none"; + + /** +- * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. ++ * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. + * +- * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac|Video Optimization - Audio Codec} ++ * [Video Optimization - Audio Codec](https://imagekit.io/docs/video-optimization#audio-codec---ac) + */ + audioCodec?: "aac" | "opus" | "none"; + + /** +- * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. ++ * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r|Effects and Enhancements - Radius} ++ * [Effects and Enhancements - Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r) + */ + radius?: number | "max"; + + /** +- * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation ++ * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation + * or `auto` to use the orientation specified in the image's EXIF data. + * For videos, only the following values are supported: 0, 90, 180, 270, or 360. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#rotate---rt|Effects and Enhancements - Rotate} ++ * [Effects and Enhancements - Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt) + */ + rotation?: number | string; + + /** +- * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. ++ * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl|Effects and Enhancements - Blur} ++ * [Effects and Enhancements - Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl) + */ + blur?: number; + + /** +- * {@link https://imagekit.io/docs/transformations#named-transformations|Transformations - Named Transformations} ++ * [Transformations - Named Transformations](https://imagekit.io/docs/transformations#named-transformations) + */ + named?: string; + + /** +- * Specifies a fallback image if the resource is not found, e.g., a URL or file path. ++ * Specifies a fallback image if the resource is not found, e.g., a URL or file path. + * +- * {@link https://imagekit.io/docs/image-transformation#default-image---di|Image Transformation - Default Image} ++ * [Image Transformation - Default Image](https://imagekit.io/docs/image-transformation#default-image---di) + */ + defaultImage?: string; + + /** +- * Flips or mirrors an image either horizontally, vertically, or both. ++ * Flips or mirrors an image either horizontally, vertically, or both. + * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl|Effects and Enhancements - Flip} ++ * [Effects and Enhancements - Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl) + */ + flip?: "h" | "v" | "h_v" | "v_h"; + + /** +- * If set to true, serves the original file without applying any transformations. ++ * If set to true, serves the original file without applying any transformations. + * +- * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true|Core Delivery Features - Deliver Original File As Is} ++ * [Core Delivery Features - Deliver Original File As Is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true) + */ + original?: boolean; + + /** +- * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. ++ * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Arithmetic expressions are also supported. + * +- * {@link https://imagekit.io/docs/trim-videos#start-offset---so|Trim Videos - Start Offset} ++ * [Trim Videos - Start Offset](https://imagekit.io/docs/trim-videos#start-offset---so) + */ + startOffset?: number | string; + + /** +- * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. ++ * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to define a time window. Arithmetic expressions are supported. + * +- * {@link https://imagekit.io/docs/trim-videos#end-offset---eo|Trim Videos - End Offset} ++ * [Trim Videos - End Offset](https://imagekit.io/docs/trim-videos#end-offset---eo) + */ + endOffset?: number | string; + + /** +- * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. ++ * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. + * +- * {@link https://imagekit.io/docs/trim-videos#duration---du|Trim Videos - Duration} ++ * [Trim Videos - Duration](https://imagekit.io/docs/trim-videos#duration---du) + */ + duration?: number | string; + + /** +- * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. ++ * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. + * +- * {@link https://imagekit.io/docs/adaptive-bitrate-streaming|Adaptive Bitrate Streaming} ++ * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) + */ + streamingResolutions?: StreamingResolution[]; + + /** +- * Enables a grayscale effect for images. ++ * Enables a grayscale effect for images. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale|Effects and Enhancements - Grayscale} ++ * [Effects and Enhancements - Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale) + */ + grayscale?: true; + + /** +- * Upscales images beyond their original dimensions using AI. Not supported inside overlay. ++ * Upscales images beyond their original dimensions using AI. Not supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} ++ * [AI Transformations - Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale) + */ + aiUpscale?: true + + /** +- * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. ++ * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} ++ * [AI Transformations - Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch) + */ + aiRetouch?: true + + /** +- * Generates a variation of an image using AI. This produces a new image with slight variations from the original, ++ * Generates a variation of an image using AI. This produces a new image with slight variations from the original, + * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} ++ * [AI Transformations - Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar) + */ + aiVariation?: true + + /** +- * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. ++ * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. + * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). + * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. + * Supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow|AI Transformations - Drop Shadow} ++ * [AI Transformations - Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow) + */ + aiDropShadow?: true | string + + /** +- * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, ++ * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, + * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Not supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg|AI Transformations - Change Background} ++ * [AI Transformations - Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg) + */ + aiChangeBackground?: string; + + /** +- * Applies ImageKit’s in-house background removal. ++ * Applies ImageKit’s in-house background removal. + * Supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} ++ * [AI Transformations - Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove) + */ + aiRemoveBackground?: true + + /** +- * Uses third-party background removal. ++ * Uses third-party background removal. + * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. + * Supported inside overlay. + * +- * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg|AI Transformations - External Background Removal} ++ * [AI Transformations - External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg) + */ + aiRemoveBackgroundExternal?: true + + /** +- * Automatically enhances the contrast of an image (contrast stretch). ++ * Automatically enhances the contrast of an image (contrast stretch). + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast|Effects and Enhancements - Contrast Stretch} ++ * [Effects and Enhancements - Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast) + */ + contrastStretch?: true + + /** +- * Adds a shadow beneath solid objects in an image with a transparent background. ++ * Adds a shadow beneath solid objects in an image with a transparent background. + * For AI-based drop shadows, refer to aiDropShadow. + * Pass `true` for a default shadow, or provide a string for a custom shadow. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow|Effects and Enhancements - Shadow} ++ * [Effects and Enhancements - Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow) + */ + shadow?: true | string + + /** +- * Sharpens the input image, highlighting edges and finer details. ++ * Sharpens the input image, highlighting edges and finer details. + * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} ++ * [Effects and Enhancements - Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen) + */ + sharpen?: true | number + + /** +- * Applies Unsharp Masking (USM), an image sharpening technique. ++ * Applies Unsharp Masking (USM), an image sharpening technique. + * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} ++ * [Effects and Enhancements - Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm) + */ + unsharpMask?: true | string; + + /** +- * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. ++ * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} ++ * [Effects and Enhancements - Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient) + */ + gradient?: true | string; + + /** +- * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, ++ * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, + * pixelated version of the full image, which gradually improves to provide a faster perceived load time. + * +- * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr|Image Optimization - Progressive Image} ++ * [Image Optimization - Progressive Image](https://imagekit.io/docs/image-optimization#progressive-image---pr) + */ + progressive?: boolean; + + /** +- * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. ++ * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. + * +- * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo|Image Optimization - Lossless Compression} ++ * [Image Optimization - Lossless Compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo) + */ + lossless?: boolean + + /** +- * Indicates whether the output image should retain the original color profile. ++ * Indicates whether the output image should retain the original color profile. + * +- * {@link https://imagekit.io/docs/image-optimization#color-profile---cp|Image Optimization - Color Profile} ++ * [Image Optimization - Color Profile](https://imagekit.io/docs/image-optimization#color-profile---cp) + */ + colorProfile?: boolean; + + /** +- * By default, ImageKit removes all metadata during automatic image compression. ++ * By default, ImageKit removes all metadata during automatic image compression. + * Set this to true to preserve metadata. + * +- * {@link https://imagekit.io/docs/image-optimization#image-metadata---md|Image Optimization - Image Metadata} ++ * [Image Optimization - Image Metadata](https://imagekit.io/docs/image-optimization#image-metadata---md) + */ + metadata?: boolean; + + /** +- * Specifies the opacity level of the output image. ++ * Specifies the opacity level of the output image. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o|Effects and Enhancements - Opacity} ++ * [Effects and Enhancements - Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o) + */ + opacity?: number; + + /** +- * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, ++ * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, + * leaving only the central object in the output image. + * +- * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t|Effects and Enhancements - Trim Edges} ++ * [Effects and Enhancements - Trim Edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t) + */ + trim?: true | number; + + /** +- * Accepts a numeric value that determines how much to zoom in or out of the cropped area. ++ * Accepts a numeric value that determines how much to zoom in or out of the cropped area. + * It should be used in conjunction with fo-face or fo-. + * +- * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z|Image Resize and Crop - Zoom} ++ * [Image Resize and Crop - Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z) + */ + zoom?: number; + + /** +- * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). ++ * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). + * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), + * or by name (e.g., `name-layer-4` for a PSD layer). + * +- * {@link https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files|Vector and Animated Images - Thumbnail Extraction} ++ * [Vector and Animated Images - Thumbnail Extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files) + */ + page?: number | string; + + /** +- * Pass any transformation not directly supported by the SDK. ++ * Pass any transformation not directly supported by the SDK. + * This transformation string is appended to the URL as provided. + */ + raw?: string; + + + /** +- * Specifies an overlay to be applied on the parent image or video. ++ * Specifies an overlay to be applied on the parent image or video. + * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. + * +- * {@link https://imagekit.io/docs/transformations#overlay-using-layers|Transformations - Overlay Using Layers} ++ * [Transformations - Overlay Using Layers](https://imagekit.io/docs/transformations#overlay-using-layers) + */ + overlay?: Overlay; + } +@@ -408,7 +409,7 @@ export interface BaseOverlay { + * Specifies the overlay's position relative to the parent asset. + * Accepts a JSON object with `x` and `y` (or `focus`) properties. + * +- * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} ++ * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) + */ + position?: OverlayPosition; + +@@ -416,7 +417,7 @@ export interface BaseOverlay { + * Specifies timing information for the overlay (only applicable if the base asset is a video). + * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. + * +- * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} ++ * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) + */ + timing?: OverlayTiming; + } +@@ -495,7 +496,7 @@ export interface TextOverlay extends BaseOverlay { + * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. + */ + +- encoding: "auto" | "plain" | "base64"; ++ encoding?: "auto" | "plain" | "base64"; + + /** + * Control styling of the text overlay. +@@ -521,12 +522,12 @@ export interface ImageOverlay extends BaseOverlay { + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain text. + */ +- encoding: "auto" | "plain" | "base64"; ++ encoding?: "auto" | "plain" | "base64"; + + /** + * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. + * +- * {@link https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay|Video} ++ * [Image](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) | [Video](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay) + */ + transformation?: Transformation[]; + } +@@ -548,12 +549,12 @@ export interface VideoOverlay extends BaseOverlay { + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain text. + */ +- encoding: "auto" | "plain" | "base64"; ++ encoding?: "auto" | "plain" | "base64"; + + /** + * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. + * +- * {@link https://imagekit.io/docs/video-transformation|Video Transformations} ++ * [Video Transformations](https://imagekit.io/docs/video-transformation) + */ + transformation?: Transformation[]; + } +@@ -575,12 +576,12 @@ export interface SubtitleOverlay extends BaseOverlay { + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain text. + */ +- encoding: "auto" | "plain" | "base64"; ++ encoding?: "auto" | "plain" | "base64"; + + /** + * Control styling of the subtitle. + * +- * {@link https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer|Styling subtitles} ++ * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + transformation?: SubtitleOverlayTransformation[]; + } +@@ -596,7 +597,7 @@ export interface SolidColorOverlay extends BaseOverlay { + /** + * Control width and height of the solid color overlay. Supported transformations depend on the base/parent asset. + * +- * {@link https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay|Video} ++ * [Image](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) | [Video](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay) + */ + transformation?: SolidColorOverlayTransformation[]; + } +diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts +index 9a45479..1eaed4b 100644 +--- a/src/interfaces/UploadOptions.ts ++++ b/src/interfaces/UploadOptions.ts +@@ -16,47 +16,74 @@ interface AbsObject { + + type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; + +-interface Transformation{ +- pre?: string +- post?: PostTransformation[] ++interface Transformation { ++ /** ++ * Specifies pre-transformations to be applied. Must be a valid string of transformations like "w-300,h-300". ++ * Refer to the docs for more details on transformations. ++ * ++ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#pre-transformation} ++ */ ++ pre?: string; ++ ++ /** ++ * Specifies post-transformations to be applied. This is an array of transformation objects, each with: ++ * - type: One of "transformation", "gif-to-video", "thumbnail", or "abs". ++ * - value: A valid transformation string required if "type" is "transformation" or "abs". Optional if "type" is "gif-to-video" or "thumbnail". ++ * - protocol: Used only when type is "abs". Can be "hls" or "dash". ++ * ++ * Refer to the docs for more details on transformations and usage in post. ++ * ++ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#post-transformation} ++ */ ++ post?: PostTransformation[]; + } ++ + /** +- * Options used when uploading a file +- * +- * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Request} ++ * Options used when uploading a file using the V1 API. ++ * Check out the official documentation: ++ * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file} + */ + 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. ++ * This field accepts three main input formats for the file content: ++ * - "binary": Directly pass the binary data. Typically used when uploading via the browser using a File or Blob. ++ * - "base64": A base64-encoded string of the file content. ++ * - "url": A direct URL from which ImageKit server will download the file and upload. + */ + file: string | Blob | File; ++ + /** +- * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. This should be in lowercase. +- * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. ++ * The name with which the file should be uploaded. ++ * - Valid characters: alphanumeric (a-z, A-Z, 0-9, including Unicode letters and numerals) and the special chars ". _ -" ++ * - Any other character (including space) is replaced with "_" ++ * ++ * Example: "company_logo.png" ++ */ ++ fileName: string; ++ ++ /** ++ * The HMAC-SHA1 digest of the concatenation of "token + expire". The signing key is your ImageKit private API key. ++ * Required for client-side authentication. Must be computed on the server side. ++ * Calculate this signature in your secure server and pass it to the client. + */ + signature: string; ++ + /** +- * A unique value generated by the client, which will be used by the ImageKit.io server 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. +- * 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. ++ * A unique value to identify and prevent replays. Typically a UUID (e.g., version 4). ++ * Each request must carry a fresh token. The server rejects reused tokens, even if the original request failed. + */ + token: string; ++ + /** +- * The time until your signature is valid. It must be a Unix time in less than 1 hour into the future. It should be in seconds. ++ * A Unix timestamp in seconds, less than 1 hour in the future. + */ + expire: number; ++ + /** +- * The name with which the file has to be uploaded. +- * The file name 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 _ ++ * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + */ +- fileName: string; ++ publicKey: string; ++ + /** + * Whether to use a unique filename for this file or not. + * - Accepts true or false. +@@ -65,85 +92,105 @@ export interface UploadOptions { + * 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. ++ * Optionally set tags on the uploaded file. ++ * If passing an array, the SDK automatically joins them into a comma-separated string when sending to the server. ++ * Example: ["t-shirt", "round-neck", "men"] => "t-shirt,round-neck,men" + */ + 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 (including unicode letters, marks, and numerals in other languages) +- * - Special Characters: / _ and - +- * - Using multiple / creates a nested folder. +- * Default value - / ++ * The folder path where the file will be stored, e.g., "/images/folder/". ++ * - If the path doesn't exist, it is created on-the-fly. ++ * - Nested folders are supported by using multiple "/". ++ * - Default: "/" + */ + 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 ++ * Whether to mark the file as private (only relevant for image uploads). ++ * A private file requires signed URLs or named transformations for access. ++ * Default: 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. ++ * A string in "x,y,width,height" format that defines the region of interest in an image (top-left coords and area size). ++ * Example: "10,10,100,100". + */ + 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. ++ * A comma-separated or array-based set of fields to return in the upload response. ++ * Example: "tags,customCoordinates,isPrivateFile,metadata" + */ + responseFields?: string | string[]; +- /* +- * Object with array of extensions to be processed on the image. ++ ++ /** ++ * An array of extension objects to apply to the image, e.g. background removal, auto-tagging, etc. ++ * The SDK will JSON-stringify this array automatically before sending. + */ + extensions?: object[]; +- /* +- * Final status of pending extensions will be sent to this URL. ++ ++ /** ++ * A webhook URL to receive the final status of any pending extensions once they've completed processing. ++ */ ++ webhookUrl?: string; ++ ++ /** ++ * Defaults to true. If false, and "useUniqueFileName" is also false, the API immediately fails if a file with the same name/folder already exists. ++ */ ++ overwriteFile?: boolean; ++ ++ /** ++ * Defaults to true. If true, and an existing file is found at the same location, its AITags are removed. Set to false to keep existing AITags. + */ +- webhookUrl?: string +- /* +- * Default is true. If overwriteFile is set to false and useUniqueFileName is also false, and a file already exists at the exact location, upload API will return an error immediately. ++ overwriteAITags?: boolean; ++ ++ /** ++ * Defaults to true. If no tags are specified in the request, existing tags are removed from overwritten files. Setting to false has no effect if the request includes tags. + */ +- overwriteFile?: boolean +- /* +- * Default is true. 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. ++ overwriteTags?: boolean; ++ ++ /** ++ * Defaults to true. If no customMetadata is specified in the request, existing customMetadata is removed from overwritten files. Setting to false has no effect if the request specifies customMetadata. + */ +- overwriteAITags?: boolean +- /* +- * Default is true. If the request does not have tags , overwriteTags is set to true and a file already exists at the exact location, existing tags will be removed. +- * In case the request body has tags, setting overwriteTags to false has no effect and request's tags are set on the asset. ++ overwriteCustomMetadata?: boolean; ++ ++ /** ++ * A stringified JSON or an object containing custom metadata fields to store with the file. ++ * Custom metadata fields must be pre-defined in your ImageKit configuration. + */ +- overwriteTags?: boolean +- /* +- * Default is true. If the request does not have customMetadata , overwriteCustomMetadata is set to true and a file already exists at the exact location, exiting customMetadata will be removed. +- * In case the request body has customMetadata, setting overwriteCustomMetadata to false has no effect and request's customMetadata is set on the asset. ++ customMetadata?: string | Record>; ++ ++ /** ++ * Defines pre and post transformations to be applied to the file during upload. The SDK enforces certain validation rules for pre/post transformations. ++ * For details, see: ++ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload} + */ +- overwriteCustomMetadata?: boolean +- /* +- * Stringified JSON key-value data to be associated with the asset. Checkout overwriteCustomMetadata parameter to understand default behaviour. +- * Before setting any custom metadata on an asset you have to create the field using custom metadata fields API. ++ transformation?: Transformation; ++ ++ /** ++ * An optional XMLHttpRequest instance for the upload. The SDK merges it with its own logic to handle progress events, etc. ++ * You can listen to `progress` or other events on this object for custom logic. + */ +- customMetadata?: string | Record> ++ xhr?: XMLHttpRequest; + +- transformation?: Transformation ++ /** ++ * A string specifying the checks to be performed server-side before uploading to the media library, e.g. size or mime type checks. ++ * For format details, see: {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#upload-api-checks} ++ */ ++ checks?: string; + + /** +- * Optional XMLHttpRequest object that you can send for upload API request. You can listen to `progress` and other events on this object for any custom logic. ++ * Optional callback function that will be called with the progress event when the file is being uploaded. + */ +- xhr?: XMLHttpRequest ++ onProgress?: (event: ProgressEvent) => void; + + /** +- * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. ++ * An AbortSignal instance that can be used to cancel the request if needed. ++ * When aborted, the request fails with an ImageKitAbortError. + */ +- checks?: string ++ abortSignal?: AbortSignal; + } +diff --git a/src/interfaces/UploadResponse.ts b/src/interfaces/UploadResponse.ts +index b38cf27..dbae9c0 100644 +--- a/src/interfaces/UploadResponse.ts ++++ b/src/interfaces/UploadResponse.ts +@@ -6,7 +6,7 @@ + * + * {@link https://imagekit.io/docs/api-reference/digital-asset-management-dam/list-and-search-assets} + */ +-export type FileType = "all" | "image" | "non-image"; ++type FileType = "all" | "image" | "non-image"; + + /** + * Metadata object structure +@@ -23,7 +23,7 @@ export type FileType = "all" | "image" | "non-image"; + * + * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on the image's contents. It is different from cryptographic hash functions like MD5 and SHA1. pHash provides similar hash value after minor distortions, like small rotations, blurring, and compression in the image. + */ +-export interface Metadata { ++interface Metadata { + height: number; + width: number; + size: number; +@@ -94,8 +94,14 @@ export interface Metadata { + }; + } + ++export interface ResponseMetadata { ++ statusCode: number; ++ requestId: string; ++ headers: Record; ++} ++ + /** +- * Response from uploading a file ++ * Response from server when file is uploaded successfully. + * + * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Responses} + */ +@@ -103,39 +109,39 @@ 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; ++ fileId?: string; + /** + * The name of the uploaded file. + */ +- name: string; ++ name?: string; + /** + * The URL of the file. + */ +- url: string; ++ url?: string; + /** + * In case of an image, a small thumbnail URL. + */ +- thumbnailUrl: string; ++ thumbnailUrl?: string; + /** + * Height of the uploaded image file. Only applicable when file type is image. + */ +- height: number; ++ height?: number; + /** + * Width of the uploaded image file. Only applicable when file type is image. + */ +- width: number; ++ width?: number; + /** + * Size of the uploaded file in bytes. + */ +- size: number; ++ size?: number; + /** + * Type of file. It can either be image or non-image. + */ +- fileType: FileType; ++ fileType?: FileType; + /** + * The path of the file uploaded. It includes any folder that you specified while uploading. + */ +- filePath: string; ++ filePath?: string; + /** + * Array of tags associated with the image. + */ +@@ -143,11 +149,11 @@ export interface UploadResponse { + /** + * Is the file marked as private. It can be either true or false. + */ +- isPrivateFile: boolean; ++ isPrivateFile?: boolean; + /** + * Value of custom coordinates associated with the image in format x,y,width,height. + */ +- customCoordinates: string | null; ++ customCoordinates?: string | null; + /** + * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. + */ +@@ -156,8 +162,21 @@ export interface UploadResponse { + * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. + */ + AITags?: object[]; ++ + /* + * Field object which will contain the status of each extension at the time of completion of the update/upload request. + */ + extensionStatus?: { [key: string]: string } ++ ++ /** ++ * Message indicating that the file upload is accepted. This field is only present when the upload is accepted but not yet processed. ++ * This can happen when the file is being processed for pre-transformation for video. ++ * The upload will be completed once the pre-transformation is done. ++ */ ++ message?: string ++ ++ /** ++ * Response metadata for debugging purposes. ++ */ ++ readonly $ResponseMetadata: ResponseMetadata; + } +diff --git a/src/interfaces/UrlOptions.ts b/src/interfaces/UrlOptions.ts +deleted file mode 100644 +index 5d1d38c..0000000 +--- a/src/interfaces/UrlOptions.ts ++++ /dev/null +@@ -1,55 +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. +- * +- * {@link https://imagekit.io/docs/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 | number }; +- /** +- * 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; +-} +- +-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 +- * +- * {@link https://github.com/imagekit-developer/imagekit-javascript#url-generation} +- */ +-export type UrlOptions = UrlOptionsSrc | UrlOptionsPath; +diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts +index 50afe5c..5aacf10 100644 +--- a/src/interfaces/index.ts ++++ b/src/interfaces/index.ts +@@ -1,7 +1,7 @@ +-import { ImageKitOptions } from "./ImageKitOptions"; +-import { TransformationPosition } from "./Transformation"; +-import { UploadOptions } from "./UploadOptions"; +-import { UploadResponse, FileType } from "./UploadResponse"; +-import { UrlOptions } from "./UrlOptions"; ++// src/interfaces/index.ts ++// Re-export all interfaces so that TypeDoc includes referenced types in the documentation + +-export type { ImageKitOptions, TransformationPosition, UploadOptions, UploadResponse, FileType, UrlOptions }; ++export * from './UploadResponse'; ++export * from './UploadOptions'; ++export * from './Transformation'; ++export * from './SrcOptions'; +diff --git a/src/upload.ts b/src/upload.ts +new file mode 100644 +index 0000000..e5a8fea +--- /dev/null ++++ b/src/upload.ts +@@ -0,0 +1,272 @@ ++import errorMessages from "./constants/errorMessages"; ++import type { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; ++ ++/** ++ * Represents an error when a request to ImageKit is invalid. ++ */ ++export class ImageKitInvalidRequestError extends Error { ++ /** ++ * Optional metadata about the response. It is only available if server returns a response. ++ */ ++ readonly $ResponseMetadata?: ResponseMetadata; ++ constructor(message: string, responseMetadata?: ResponseMetadata) { ++ super(message); ++ this.name = "ImageKitInvalidRequestError"; ++ this.$ResponseMetadata = responseMetadata; ++ } ++} ++ ++/** ++ * Represents an error when an upload operation is aborted. ++ */ ++export class ImageKitAbortError extends Error { ++ /** ++ * The reason why the operation was aborted, which can be any JavaScript value. If not specified, the reason is set to "AbortError" DOMException. ++ */ ++ reason?: unknown; ++ constructor(message: string, reason?: unknown) { ++ super(message); ++ this.name = "ImageKitAbortError"; ++ this.reason = reason; ++ } ++} ++ ++/** ++ * Represents a network error during an upload operation to ImageKit. ++ */ ++export class ImageKitUploadNetworkError extends Error { ++ constructor(message: string) { ++ super(message); ++ this.name = "ImageKitUploadNetworkError"; ++ } ++} ++ ++/** ++ * Represents a server error from ImageKit during an upload operation. ++ */ ++export class ImageKitServerError extends Error { ++ /** ++ * Optional metadata about the response. It is only available if server returns a response. ++ */ ++ readonly $ResponseMetadata?: ResponseMetadata; ++ constructor(message: string, responseMetadata?: ResponseMetadata) { ++ super(message); ++ this.name = "ImageKitServerError"; ++ this.$ResponseMetadata = responseMetadata; ++ } ++} ++ ++/** ++ * Uploads a file to ImageKit with the given upload options. This function uses V1 API, check the [API docs](https://imagekit.io/docs/api-reference/upload-file/upload-file) for more details. ++ * ++ * @throws {ImageKitInvalidRequestError} If the request is invalid. ++ * @throws {ImageKitAbortError} If the request is aborted. ++ * @throws {ImageKitUploadNetworkError} If there is a network error. ++ * @throws {ImageKitServerError} If there is a server error. ++ * ++ * @param {UploadOptions} uploadOptions - The options for uploading the file. ++ * @returns {Promise} A Promise resolving to a successful UploadResponse. ++ */ ++export const upload = (uploadOptions: UploadOptions): Promise => { ++ if(!uploadOptions) { ++ return Promise.reject(new ImageKitInvalidRequestError("Invalid options provided for upload")); ++ } ++ return new Promise((resolve, reject) => { ++ const { xhr: userProvidedXHR } = uploadOptions || {}; ++ delete uploadOptions.xhr; ++ const xhr = userProvidedXHR || new XMLHttpRequest(); ++ ++ if (!uploadOptions.file) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILE_PARAMETER.message)); ++ } ++ ++ if (!uploadOptions.fileName) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER.message)); ++ } ++ ++ if (!uploadOptions.publicKey || uploadOptions.publicKey.length === 0) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_PUBLIC_KEY.message)); ++ } ++ ++ if (!uploadOptions.token) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_TOKEN.message)); ++ } ++ ++ if (!uploadOptions.signature) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_SIGNATURE.message)); ++ } ++ ++ if (!uploadOptions.expire) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_EXPIRE.message)); ++ } ++ ++ if (uploadOptions.transformation) { ++ if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_TRANSFORMATION.message)); ++ } ++ if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_PRE_TRANSFORMATION.message)); ++ } ++ 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)) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); ++ } else if (transformation.type === "transformation" && !transformation.value) { ++ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); ++ } ++ } ++ } else { ++ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); ++ } ++ } ++ } ++ ++ var formData = new FormData(); ++ let key: keyof typeof uploadOptions; ++ for (key in uploadOptions) { ++ if (key) { ++ if (key === "file" && typeof uploadOptions.file != "string") { ++ formData.set('file', uploadOptions.file, String(uploadOptions.fileName)); ++ } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { ++ formData.set('tags', uploadOptions.tags.join(",")); ++ } else if (key === 'signature') { ++ formData.set("signature", uploadOptions.signature); ++ } else if (key === 'expire') { ++ formData.set("expire", String(uploadOptions.expire)); ++ } else if (key === 'token') { ++ formData.set("token", uploadOptions.token); ++ } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { ++ formData.set('responseFields', uploadOptions.responseFields.join(",")); ++ } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { ++ formData.set('extensions', JSON.stringify(uploadOptions.extensions)); ++ } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && ++ !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { ++ formData.set('customMetadata', JSON.stringify(uploadOptions.customMetadata)); ++ } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && ++ uploadOptions.transformation !== null) { ++ formData.set(key, JSON.stringify(uploadOptions.transformation)); ++ } else if (key === 'checks' && uploadOptions.checks) { ++ formData.set("checks", uploadOptions.checks); ++ } else if (uploadOptions[key] !== undefined) { ++ if (["onProgress", "abortSignal"].includes(key)) continue; ++ formData.set(key, String(uploadOptions[key])); ++ } ++ } ++ } ++ ++ if (uploadOptions.onProgress) { ++ xhr.upload.onprogress = function (event: ProgressEvent) { ++ if (uploadOptions.onProgress) uploadOptions.onProgress(event) ++ }; ++ } ++ ++ function onAbortHandler() { ++ xhr.abort(); ++ return reject(new ImageKitAbortError( ++ "Upload aborted", ++ uploadOptions.abortSignal?.reason ++ )); ++ } ++ ++ if (uploadOptions.abortSignal) { ++ if (uploadOptions.abortSignal.aborted) { ++ // If the signal is already aborted, return immediately with the reason ++ ++ return reject(new ImageKitAbortError( ++ "Upload aborted", ++ uploadOptions.abortSignal?.reason ++ )); ++ } ++ ++ // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted ++ uploadOptions.abortSignal.addEventListener("abort", onAbortHandler); ++ ++ // On XHR completion (success, fail, or abort), remove just this abort handler ++ xhr.addEventListener("loadend", () => { ++ if (uploadOptions.abortSignal) { ++ uploadOptions.abortSignal.removeEventListener("abort", onAbortHandler); ++ } ++ }); ++ } ++ ++ xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); ++ xhr.onerror = function (e) { ++ return reject(new ImageKitUploadNetworkError(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR.message)); ++ } ++ xhr.onload = function () { ++ if (xhr.status >= 200 && xhr.status < 300) { ++ try { ++ var body = JSON.parse(xhr.responseText); ++ var uploadResponse = addResponseHeadersAndBody(body, xhr); ++ return resolve(uploadResponse); ++ } catch (ex: any) { ++ return reject(ex); ++ } ++ } else if (xhr.status >= 400 && xhr.status < 500) { ++ // Send ImageKitInvalidRequestError ++ try { ++ var body = JSON.parse(xhr.responseText); ++ return reject(new ImageKitInvalidRequestError( ++ body.message ?? "Invalid request. Please check the parameters.", ++ getResponseMetadata(xhr) ++ )); ++ } catch (ex: any) { ++ return reject(ex); ++ } ++ } else { ++ // Send ImageKitServerError ++ try { ++ var body = JSON.parse(xhr.responseText); ++ return reject(new ImageKitServerError( ++ body.message ?? "Server error occurred while uploading the file. This is rare and usually temporary.", ++ getResponseMetadata(xhr) ++ )); ++ } catch (ex: any) { ++ return reject(new ImageKitServerError( ++ "Server error occurred while uploading the file. This is rare and usually temporary.", ++ getResponseMetadata(xhr) ++ )); ++ } ++ } ++ }; ++ xhr.send(formData); ++ }); ++}; ++ ++ ++const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest) => { ++ let response = { ...body }; ++ const responseMetadata = getResponseMetadata(xhr); ++ Object.defineProperty(response, "$ResponseMetadata", { ++ value: responseMetadata, ++ enumerable: false, ++ writable: false ++ }); ++ return response; ++} ++ ++const getResponseMetadata = (xhr: XMLHttpRequest): ResponseMetadata => { ++ const headers = getResponseHeaderMap(xhr); ++ const responseMetadata = { ++ statusCode: xhr.status, ++ headers: headers, ++ requestId: headers["x-request-id"] ++ } ++ return responseMetadata; ++} ++ ++function getResponseHeaderMap(xhr: XMLHttpRequest): Record { ++ const headers: Record = {}; ++ const responseHeaders = xhr.getAllResponseHeaders(); ++ if (Object.keys(responseHeaders).length) { ++ responseHeaders ++ .trim() ++ .split(/[\r\n]+/) ++ .map(value => value.split(/: /)) ++ .forEach(keyValue => { ++ headers[keyValue[0].trim().toLowerCase()] = keyValue[1].trim(); ++ }); ++ } ++ return headers; ++} +diff --git a/src/upload/index.ts b/src/upload/index.ts +deleted file mode 100644 +index 3d4915a..0000000 +--- a/src/upload/index.ts ++++ /dev/null +@@ -1,104 +0,0 @@ +-import errorMessages from "../constants/errorMessages"; +-import respond from "../utils/respond"; +-import { request } from "../utils/request"; +-import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; +- +-export const upload = ( +- xhr: XMLHttpRequest, +- uploadOptions: UploadOptions, +- options: ImageKitOptions, +- callback?: (err: Error | null, response: UploadResponse | null) => void, +-) => { +- 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 (!options.publicKey) { +- respond(true, errorMessages.MISSING_PUBLIC_KEY, callback); +- return; +- } +- +- if(!uploadOptions.token) { +- respond(true, errorMessages.MISSING_TOKEN, callback) +- return +- } +- +- if(!uploadOptions.signature) { +- respond(true, errorMessages.MISSING_SIGNATURE, callback) +- return +- } +- +- if(!uploadOptions.expire) { +- respond(true, errorMessages.MISSING_EXPIRE, 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 = new FormData(); +- let key: keyof typeof uploadOptions; +- for (key in uploadOptions) { +- if (key) { +- if (key === "file" && typeof uploadOptions.file != "string") { +- formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); +- } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { +- formData.append('tags', uploadOptions.tags.join(",")); +- } else if (key === 'signature') { +- formData.append("signature", uploadOptions.signature); +- } else if (key === 'expire') { +- formData.append("expire", String(uploadOptions.expire)); +- } else if (key === 'token') { +- formData.append("token", uploadOptions.token); +- } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { +- formData.append('responseFields', uploadOptions.responseFields.join(",")); +- } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { +- formData.append('extensions', JSON.stringify(uploadOptions.extensions)); +- } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && +- !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { +- formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); +- } else if(key === "transformation" && typeof uploadOptions.transformation === "object" && +- uploadOptions.transformation !== null) { +- formData.append(key, JSON.stringify(uploadOptions.transformation)); +- } else if (key === 'checks' && uploadOptions.checks) { +- formData.append("checks", uploadOptions.checks); +- } else if(uploadOptions[key] !== undefined) { +- formData.append(key, String(uploadOptions[key])); +- } +- } +- } +- +- formData.append("publicKey", options.publicKey); +- +- request(xhr, formData, callback); +-}; +diff --git a/src/url/builder.ts b/src/url.ts +similarity index 83% +rename from src/url/builder.ts +rename to src/url.ts +index cc49a1b..836bb8e 100644 +--- a/src/url/builder.ts ++++ b/src/url.ts +@@ -1,6 +1,6 @@ +-import { ImageKitOptions, UrlOptions } from "../interfaces"; +-import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; +-import transformationUtils, { safeBtoa } from "../utils/transformation"; ++import type { SrcOptions } from "./interfaces"; ++import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; ++import transformationUtils, { safeBtoa } from "./utils/transformation"; + const TRANSFORMATION_PARAMETER = "tr"; + const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') + const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. +@@ -25,17 +25,29 @@ function pathJoin(parts: string[], sep?: string) { + return parts.join(separator).replace(replace, separator); + } + +-export const buildURL = (opts: UrlOptions & ImageKitOptions) => { +- if (!opts.path && !opts.src) { ++/** ++ * Builds a source URL with the given options. ++ * ++ * @param {SrcOptions} opts - The options for building the source URL. ++ * @returns {string} The constructed source URL. ++ */ ++export const buildSrc = (opts: SrcOptions): string => { ++ opts.urlEndpoint = opts.urlEndpoint || ""; ++ opts.src = opts.src || ""; ++ opts.transformationPosition = opts.transformationPosition || "query"; ++ ++ if (!opts.src) { + return ""; + } + ++ const isAbsoluteURL = opts.src.startsWith("http://") || opts.src.startsWith("https://"); ++ + var urlObj, isSrcParameterUsedForURL, urlEndpointPattern; + + try { +- if (opts.path) { ++ if (!isAbsoluteURL) { + urlEndpointPattern = new URL(opts.urlEndpoint).pathname; +- urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.path])); ++ urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.src])); + } else { + urlObj = new URL(opts.src!); + isSrcParameterUsedForURL = true; +@@ -49,7 +61,7 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { + urlObj.searchParams.append(i, String(opts.queryParameters[i])); + } + +- var transformationString = constructTransformationString(opts.transformation); ++ var transformationString = buildTransformationString(opts.transformation); + + if (transformationString && transformationString.length) { + if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { +@@ -57,7 +69,7 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { + TRANSFORMATION_PARAMETER + transformationUtils.getChainTransformDelimiter() + transformationString, + urlObj.pathname, + ]); +- } ++ } + } + + if (urlEndpointPattern) { +@@ -67,8 +79,8 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { + } + + if (transformationString && transformationString.length) { +- if(transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { +- if(urlObj.searchParams.toString() !== "") { // In 12 node.js .size was not there. So, we need to check if it is an object or not. ++ if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { ++ if (urlObj.searchParams.toString() !== "") { // In 12 node.js .size was not there. So, we need to check if it is an object or not. + return `${urlObj.href}&${TRANSFORMATION_PARAMETER}=${transformationString}`; + } + else { +@@ -83,10 +95,10 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { + function processInputPath(str: string, enccoding: string): string { + // Remove leading and trailing slashes + str = removeTrailingSlash(removeLeadingSlash(str)); +- if(enccoding === "plain") { ++ if (enccoding === "plain") { + return `i-${str.replace(/\//g, "@@")}`; + } +- if(enccoding === "base64") { ++ if (enccoding === "base64") { + return `ie-${encodeURIComponent(safeBtoa(str))}`; + } + if (SIMPLE_OVERLAY_PATH_REGEX.test(str)) { +@@ -111,13 +123,11 @@ function processText(str: string, enccoding: TextOverlay["encoding"]): string { + + function processOverlay(overlay: Transformation["overlay"]): string | undefined { + const entries = []; +- if (!overlay) { +- return; +- } +- const { type, position = {}, timing = {}, transformation = [] } = overlay; ++ ++ const { type, position = {}, timing = {}, transformation = [] } = overlay || {}; + + if (!type) { +- throw new Error("Overlay type is required"); ++ return; + } + + switch (type) { +@@ -205,7 +215,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined + entries.push(`ldu-${duration}`); + } + +- const transformationString = constructTransformationString(transformation); ++ const transformationString = buildTransformationString(transformation); + + if (transformationString && transformationString.trim() !== "") entries.push(transformationString); + +@@ -214,7 +224,13 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined + return entries.join(transformationUtils.getTransformDelimiter()); + } + +-function constructTransformationString(transformation: Transformation[] | undefined) { ++/** ++ * Builds a transformation string from the given transformations. ++ * ++ * @param {Transformation[] | undefined} transformation - The transformations to apply. ++ * @returns {string} The constructed transformation string. ++ */ ++export const buildTransformationString = function (transformation: Transformation[] | undefined): string { + if (!Array.isArray(transformation)) { + return ""; + } +diff --git a/src/url/index.ts b/src/url/index.ts +deleted file mode 100644 +index 8503b76..0000000 +--- a/src/url/index.ts ++++ /dev/null +@@ -1,12 +0,0 @@ +-/* +- URL builder +-*/ +-import { ImageKitOptions, UrlOptions } from "../interfaces"; +-import { buildURL } from "./builder"; +- +-export const url = (urlOpts: UrlOptions, defaultOptions: ImageKitOptions) => { +- return buildURL({ +- ...defaultOptions, +- ...urlOpts, +- }); +-}; +diff --git a/src/utils/request.ts b/src/utils/request.ts +deleted file mode 100644 +index fd7688d..0000000 +--- a/src/utils/request.ts ++++ /dev/null +@@ -1,83 +0,0 @@ +-import respond from "../utils/respond"; +-import errorMessages from "../constants/errorMessages" +-import { ImageKitOptions, UploadResponse } from "../interfaces"; +-import IKResponse from "../interfaces/IKResponse"; +- +-interface SignatureResponse { +- signature: string +- expire: number +- token: string +-} +- +-function getResponseHeaderMap(xhr: XMLHttpRequest) { +- const headers: Record = {}; +- const responseHeaders = xhr.getAllResponseHeaders(); +- if (Object.keys(responseHeaders).length) { +- responseHeaders +- .trim() +- .split(/[\r\n]+/) +- .map(value => value.split(/: /)) +- .forEach(keyValue => { +- headers[keyValue[0].trim()] = keyValue[1].trim(); +- }); +- } +- return headers; +-} +- +-const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse => { +- let response = { ...body }; +- const responseMetadata = { +- statusCode: xhr.status, +- headers: getResponseHeaderMap(xhr) +- } +- Object.defineProperty(response, "$ResponseMetadata", { +- value: responseMetadata, +- enumerable: false, +- writable: false +- }); +- return response as IKResponse; +-} +- +-export const request = ( +- uploadFileXHR: XMLHttpRequest, +- formData: FormData, +- callback?: (err: Error | null, response: UploadResponse | null) => void) => { +- +- uploadFile(uploadFileXHR, formData).then((result) => { +- return respond(false, result, callback); +- }, (ex) => { +- return respond(true, ex, callback); +- }); +-} +- +-export const uploadFile = ( +- uploadFileXHR: XMLHttpRequest, +- formData: FormData +-): Promise | Error> => { +- return new Promise((resolve, reject) => { +- uploadFileXHR.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); +- uploadFileXHR.onerror = function (e) { +- return reject(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR); +- } +- uploadFileXHR.onload = function () { +- if (uploadFileXHR.status === 200) { +- try { +- var body = JSON.parse(uploadFileXHR.responseText); +- var uploadResponse = addResponseHeadersAndBody(body, uploadFileXHR); +- return resolve(uploadResponse); +- } catch (ex: any) { +- return reject(ex); +- } +- } else { +- try { +- var body = JSON.parse(uploadFileXHR.responseText); +- var uploadError = addResponseHeadersAndBody(body, uploadFileXHR); +- return reject(uploadError) +- } catch (ex: any) { +- return reject(ex); +- } +- } +- }; +- uploadFileXHR.send(formData); +- }); +-} +diff --git a/src/utils/respond.ts b/src/utils/respond.ts +deleted file mode 100644 +index 06d02f6..0000000 +--- a/src/utils/respond.ts ++++ /dev/null +@@ -1,9 +0,0 @@ +-export default function(isError: boolean, response: any, callback?: (err: Error | null, response: any) => void) { +- if(typeof callback == "function") { +- if(isError) { +- callback(response, null); +- } else { +- callback(null, response); +- } +- } +-}; +\ No newline at end of file +diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts +index 5034d0f..324fef0 100644 +--- a/src/utils/transformation.ts ++++ b/src/utils/transformation.ts +@@ -1,25 +1,16 @@ + import supportedTransforms from "../constants/supportedTransforms"; +-import { ImageKitOptions, TransformationPosition } from "../interfaces"; ++import { TransformationPosition, SrcOptions } from "../interfaces"; + + const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; + const PATH_TRANSFORMATION_POSITION: TransformationPosition = "path"; +-const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = QUERY_TRANSFORMATION_POSITION; +-const VALID_TRANSFORMATION_POSITIONS = [PATH_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; + const CHAIN_TRANSFORM_DELIMITER: string = ":"; + const TRANSFORM_DELIMITER: string = ","; + const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; + + export default { +- getDefault: (): TransformationPosition => { +- return DEFAULT_TRANSFORMATION_POSITION; +- }, +- addAsQueryParameter: (options: ImageKitOptions) => { ++ addAsQueryParameter: (options: SrcOptions) => { + return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; + }, +- validParameters: (options: ImageKitOptions) => { +- if (typeof options.transformationPosition == "undefined") return false; +- return VALID_TRANSFORMATION_POSITIONS.indexOf(options.transformationPosition) != -1; +- }, + getTransformKey: function (transform: string) { + if (!transform) { return ""; } + +@@ -38,6 +29,7 @@ export default { + + export const safeBtoa = function (str: string): string { + if (typeof window !== "undefined") { ++ /* istanbul ignore next */ + return btoa(str); + } else { + // Node fallback +diff --git a/test/data/index.js b/test/data/index.js +deleted file mode 100644 +index 1ec1645..0000000 +--- a/test/data/index.js ++++ /dev/null +@@ -1,5 +0,0 @@ +-module.exports.initializationParams = { +- publicKey: "test_public_key", +- urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", +- transformationPosition: "path", +-} +\ No newline at end of file +diff --git a/test/initialization.js b/test/initialization.js +deleted file mode 100644 +index b3695ed..0000000 +--- a/test/initialization.js ++++ /dev/null +@@ -1,70 +0,0 @@ +-const chai = require("chai"); +-const expect = chai.expect; +-const initializationParams = require("./data").initializationParams +-import ImageKit from "../src/index"; +- +- +-describe("Initialization checks", function () { +- var imagekit = new ImageKit(initializationParams); +- +- it('should throw error: options - empty object', function () { +- try { +- new ImageKit({}); +- } catch(err) { +- expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); +- } +- }); +- +- it('should throw error: options - undefined', function () { +- try { +- new ImageKit(); +- } catch(err) { +- expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); +- } +- }); +- +- it('Pass private Key', function () { +- try { +- new ImageKit({ +- urlEndpoint: initializationParams.urlEndpoint, +- privateKey: "should_not_pass" +- }); +- } catch(err) { +- expect(err.message).to.be.equal('privateKey should not be passed on the client side'); +- } +- }); +- +- 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); +- }); +- +- 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'); +- }); +- +- it('only urlEndpoint is required parameter', function () { +- let imagekit = new ImageKit({ +- urlEndpoint: initializationParams.urlEndpoint +- }); +- +- expect(imagekit.options).to.be.an('object'); +- expect(imagekit.options).to.have.property('urlEndpoint').to.be.equal(initializationParams.urlEndpoint); +- expect(imagekit.url).to.exist.and.to.be.a('function'); +- expect(imagekit.upload).to.exist.and.to.be.a('function'); +- +- }); +- +- it('should throw error: invalid transformationPosition', function () { +- try { +- new ImageKit({...initializationParams, transformationPosition: "test"}); +- } catch(err) { +- expect(err.message).to.be.equal('Invalid transformationPosition parameter'); +- } +- }); +-}); +\ No newline at end of file +diff --git a/test/setup.js b/test/setup.js +new file mode 100644 +index 0000000..5decfdd +--- /dev/null ++++ b/test/setup.js +@@ -0,0 +1,12 @@ ++// test-setup.js (loaded before your tests) ++global.FormData = require("formdata-node"); ++global.Blob = require("web-file-polyfill").Blob ++global.File = require("web-file-polyfill").File ++global.ProgressEvent = class FakeProgressEvent { ++ constructor(type, init = {}) { ++ this.type = type; ++ this.lengthComputable = init.lengthComputable || false; ++ this.loaded = init.loaded || 0; ++ this.total = init.total || 0; ++ } ++}; +diff --git a/test/upload.js b/test/upload.js +index 5f27bd4..a5c7004 100644 +--- a/test/upload.js ++++ b/test/upload.js +@@ -1,13 +1,15 @@ + const chai = require("chai"); + const sinon = require("sinon"); +-global.FormData = require("formdata-node"); +-global.Blob = require("web-file-polyfill").Blob +-global.File = require("web-file-polyfill").File + const expect = chai.expect; +-const initializationParams = require("./data").initializationParams +-import ImageKit from "../src/index"; ++import 'regenerator-runtime/runtime'; ++import { ++ ImageKitAbortError, ++ ImageKitInvalidRequestError, ++ ImageKitServerError, ++ ImageKitUploadNetworkError, upload ++} from "../src/index"; ++ + var requests, server; +-import 'regenerator-runtime/runtime' + + const uploadSuccessResponseObj = { + "fileId": "598821f949c0a938d57563bd", +@@ -29,8 +31,9 @@ const uploadSuccessResponseObj = { + const securityParameters = { + signature: "test_signature", + expire: 123, +- token: "test_token" +-} ++ token: "test_token", ++ publicKey: "test_public_key", ++}; + + function successUploadResponse() { + server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", +@@ -59,17 +62,13 @@ function errorUploadResponse(statusCode, obj) { + } + + async function sleep(ms = 0) { +- return new Promise((resolve, reject) => { +- setTimeout(() => { +- resolve(); +- }, ms); ++ return true; ++ return new Promise((resolve) => { ++ setTimeout(resolve, ms); + }); + } + +-describe("File upload", function () { +- +- var imagekit = new ImageKit(initializationParams); +- ++describe("File upload", async function () { + beforeEach(() => { + global.XMLHttpRequest = sinon.useFakeXMLHttpRequest(); + requests = []; +@@ -78,130 +77,161 @@ describe("File upload", function () { + }); + + afterEach(() => { +- // Like before we must clean up when tampering with globals. + global.XMLHttpRequest.restore(); + server.restore(); + }); + +- it('Invalid options', function () { +- var callback = sinon.spy(); +- +- imagekit.upload(undefined, callback); +- expect(server.requests.length).to.be.equal(0); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { help: "", message: "Invalid uploadOptions parameter" }, null); ++ it('Invalid options', async function () { ++ try { ++ await upload(); ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid options provided for upload"); ++ } + }); + +- it('Missing fileName', function () { ++ it('Missing fileName', async function () { + const fileOptions = { + ...securityParameters, + file: "https://ik.imagekit.io/remote-url.jpg" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { help: "", message: "Missing fileName parameter for upload" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing fileName parameter for upload"); ++ } + }); + +- it('Missing file', function () { ++ it('Missing file', async function () { + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing file parameter for upload"); ++ } + }); +- +- it('Missing token', function () { ++ ++ it('Missing token', async function () { + const fileOptions = { + fileName: "test_file_name", + file: "test_file", + signature: 'test_signature', +- expire: 123 ++ expire: 123, ++ // Omit token ++ publicKey: 'test_public_key' + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing token for upload. The SDK expects token, signature and expire for authentication."); ++ } + }); + +- it('Missing signature', function () { ++ it('Missing signature', async function () { + const fileOptions = { + fileName: "test_file_name", + file: "test_file", + token: 'test_token', +- expire: 123 ++ expire: 123, ++ publicKey: 'test_public_key' ++ // Omit signature + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing signature for upload. The SDK expects token, signature and expire for authentication."); ++ } + }); + +- it('Missing expire', function () { ++ it('Missing expire', async function () { + const fileOptions = { + fileName: "test_file_name", + file: "test_file", + token: 'test_token', +- signature: 'test_signature' ++ signature: 'test_signature', ++ publicKey: 'test_public_key' ++ // Omit expire + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing expire for upload. The SDK expects token, signature and expire for authentication."); ++ } + }); + +- it('Missing public key', function () { ++ it('Missing public key', async function () { + const fileOptions = { + fileName: "test_file_name", +- file: "test_file" ++ file: "test_file", ++ token: 'test_token', ++ signature: 'test_signature', ++ expire: 123 ++ // Omit publicKey + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback, { +- publicKey: "" +- }); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); +- sinon.assert.calledWith(callback, { message: "Missing public key for upload", help: "" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Missing public key for upload"); ++ } + }); + + it('Upload endpoint network error handling', async function () { + const fileOptions = { +- ...securityParameters, + fileName: "test_file_name", +- file: "test_file" ++ file: "test_file", ++ token: 'test_token', ++ signature: 'test_signature', ++ expire: 123, ++ publicKey: 'test_public_key' + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); +- + // Simulate network error on upload API + server.requests[0].error(); + await sleep(); +- sinon.assert.calledWith(callback, { message: "Request to ImageKit upload endpoint failed due to network error", help: "" }, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitUploadNetworkError).to.be.true; ++ expect(ex.message).to.be.equal("Request to ImageKit upload endpoint failed due to network error"); ++ } + }); + + it('Boolean handling', async function () { +@@ -216,10 +246,7 @@ describe("File upload", function () { + isPrivateFile: true + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -238,8 +265,8 @@ describe("File upload", function () { + expect(arg.get('isPrivateFile')).to.be.equal('true'); + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Tag array handling', async function () { +@@ -252,10 +279,7 @@ describe("File upload", function () { + isPrivateFile: true + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -272,8 +296,8 @@ describe("File upload", function () { + expect(arg.get('isPrivateFile')).to.be.equal('true'); + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Missing useUniqueFileName', async function () { +@@ -285,10 +309,7 @@ describe("File upload", function () { + isPrivateFile: true + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -307,8 +328,8 @@ describe("File upload", function () { + expect(arg.get('customCoordinates')).to.be.equal(undefined); + expect(arg.get('responseFields')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Missing isPrivateFile', async function () { +@@ -319,10 +340,7 @@ describe("File upload", function () { + tags: ["test_tag1", "test_tag2"] + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -341,8 +359,8 @@ describe("File upload", function () { + expect(arg.get('customCoordinates')).to.be.equal(undefined); + expect(arg.get('responseFields')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('With extensions parameter', async function () { +@@ -364,10 +382,7 @@ describe("File upload", function () { + ], + webhookUrl: "https://your-domain/?appId=some-id" + }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -387,10 +402,10 @@ describe("File upload", function () { + expect(arg.get('isPrivateFile')).to.be.equal('true'); + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); +- expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id') ++ expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id'); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Bare minimum request', async function () { +@@ -401,10 +416,7 @@ describe("File upload", function () { + tags: undefined + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -423,29 +435,26 @@ describe("File upload", function () { + expect(arg.get('customCoordinates')).to.be.equal(undefined); + expect(arg.get('responseFields')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Bare minimum request: Blob', async function () { +- const buffer = Buffer.from("test_buffer") ++ const buffer = Buffer.from("test_buffer"); + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", + file: buffer + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); + await sleep(); + + var arg = server.requests[0].requestBody; +- ++ // It's a blob now, check size + expect(arg.get('file').size).to.be.eq(buffer.length); + expect(arg.get('fileName')).to.be.equal("test_file_name"); + expect(arg.get('token')).to.be.equal("test_token"); +@@ -458,8 +467,8 @@ describe("File upload", function () { + expect(arg.get('customCoordinates')).to.be.equal(undefined); + expect(arg.get('responseFields')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Error during upload', async function () { +@@ -469,20 +478,22 @@ describe("File upload", function () { + file: "test_file" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + var errRes = { + help: "For support kindly contact us at support@imagekit.io .", + message: "Your account cannot be authenticated." +- } +- errorUploadResponse(500, errRes); ++ }; ++ errorUploadResponse(401, errRes); + await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Your account cannot be authenticated."); ++ } + }); + + it('Error during upload non 2xx with bad body', async function () { +@@ -492,10 +503,7 @@ describe("File upload", function () { + file: "test_file" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", +@@ -507,9 +515,14 @@ describe("File upload", function () { + ); + server.respond(); + await sleep(); +- expect(callback.calledOnce).to.be.true; +- var error = callback.args[0][0]; +- expect(error instanceof SyntaxError).to.be.true; ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ // The response body is invalid JSON => SyntaxError ++ expect(ex instanceof ImageKitServerError).to.be.true; ++ expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); ++ } + }); + + it('Error during upload 2xx with bad body', async function () { +@@ -519,10 +532,7 @@ describe("File upload", function () { + file: "test_file" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", +@@ -534,9 +544,12 @@ describe("File upload", function () { + ); + server.respond(); + await sleep(); +- expect(callback.calledOnce).to.be.true; +- var error = callback.args[0][0]; +- expect(error instanceof SyntaxError).to.be.true; ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof SyntaxError).to.be.true; ++ } + }); + + it('Upload via URL', async function () { +@@ -546,10 +559,7 @@ describe("File upload", function () { + file: "https://ik.imagekit.io/remote-url.jpg" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -568,8 +578,8 @@ describe("File upload", function () { + expect(arg.get('customCoordinates')).to.be.equal(undefined); + expect(arg.get('responseFields')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Overriding public key', async function () { +@@ -581,9 +591,8 @@ describe("File upload", function () { + file: "https://ik.imagekit.io/remote-url.jpg" + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback, { ++ const uploadPromise = upload({ ++ ...fileOptions, + publicKey: newPublicKey + }); + +@@ -607,8 +616,8 @@ describe("File upload", function () { + expect(arg.get('extensions')).to.be.equal(undefined); + expect(arg.get('customMetadata')).to.be.equal(undefined); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('With overwrite parameters', async function () { +@@ -622,21 +631,14 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ], + overwriteFile: false, + overwriteAITags: false, + overwriteTags: false, + overwriteCustomMetadata: false + }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -661,8 +663,8 @@ describe("File upload", function () { + expect(arg.get('overwriteTags')).to.be.equal('false'); + expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('With customMetadata', async function () { +@@ -676,11 +678,7 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ], + overwriteFile: false, + overwriteAITags: false, +@@ -691,10 +689,7 @@ describe("File upload", function () { + color: "red" + }, + }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -720,8 +715,8 @@ describe("File upload", function () { + expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); + expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Array type fields', async function () { +@@ -735,11 +730,7 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ], + overwriteFile: false, + overwriteAITags: false, +@@ -750,10 +741,7 @@ describe("File upload", function () { + color: "red" + }, + }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -779,8 +767,8 @@ describe("File upload", function () { + expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); + expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('check custom XHR object is used', async function () { +@@ -797,16 +785,11 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ], + xhr + }; +- var callback = sinon.spy(); +- imagekit.upload(fileOptions, callback); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + expect(server.requests[0]).to.be.equal(xhr); + expect(server.requests[0].onprogress.toString()).to.be.equal(fun.toString()); +@@ -829,8 +812,8 @@ describe("File upload", function () { + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); + +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('Upload using promise - success', async function () { +@@ -844,15 +827,11 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ] + }; + +- var uploadPromise = imagekit.upload(fileOptions); ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + + await sleep(); +@@ -873,15 +852,15 @@ describe("File upload", function () { + expect(arg.get('isPrivateFile')).to.be.equal('true'); + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); +- var response = await uploadPromise; ++ const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + +- it('Upload using promise - error', async function () { ++ it('Server 5xx error with proper json and message', async function () { + var errRes = { + help: "For support kindly contact us at support@imagekit.io .", +- message: "Your account cannot be authenticated." +- } ++ message: "Something went wrong" ++ }; + const fileOptions = { + ...securityParameters, + fileName: "test_file_name", +@@ -892,22 +871,20 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ] + }; + + try { +- var uploadPromise = imagekit.upload(fileOptions); ++ const uploadPromise = upload(fileOptions); + await sleep(); + errorUploadResponse(500, errRes); + await sleep(); +- var response = await uploadPromise; ++ await uploadPromise; ++ throw new Error('Should have thrown error'); + } catch (ex) { +- expect(ex).to.be.deep.equal(errRes); ++ expect(ex instanceof ImageKitServerError).to.be.true; ++ expect(ex.message).to.be.equal("Something went wrong"); + } + }); + +@@ -925,19 +902,14 @@ describe("File upload", function () { + useUniqueFileName: false, + isPrivateFile: true, + extensions: [ +- { +- name: "aws-auto-tagging", +- minConfidence: 80, +- maxTags: 10 +- } ++ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } + ], + xhr + }; +- var uploadPromise = imagekit.upload(fileOptions); ++ const uploadPromise = upload(fileOptions); + + expect(server.requests.length).to.be.equal(1); + +- + await sleep(); + successUploadResponse(); + await sleep(); +@@ -960,13 +932,13 @@ describe("File upload", function () { + expect(arg.get('publicKey')).to.be.equal('test_public_key'); + expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); + +- var response = await uploadPromise; ++ const response = await uploadPromise; + expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it('$ResponseMetadata assertions using promise', async function () { +- var dummyResonseHeaders = { +- "Content-Type": "application/json", ++ var dummyResponseHeaders = { ++ "content-type": "application/json", + "x-request-id": "sdfsdfsdfdsf" + }; + const fileOptions = { +@@ -987,7 +959,7 @@ describe("File upload", function () { + ] + }; + +- var uploadPromise = imagekit.upload(fileOptions) ++ var uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + + await sleep(); +@@ -995,53 +967,17 @@ describe("File upload", function () { + server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", + [ + 200, +- dummyResonseHeaders, +- JSON.stringify(uploadSuccessResponseObj) +- ] +- ); +- server.respond(); +- await sleep(); +- +- var response = await uploadPromise; +- expect(response.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); +- expect(response.$ResponseMetadata.statusCode).to.be.deep.equal(200); +- }); +- +- it('$ResponseMetadata assertions using callback', async function () { +- var dummyResonseHeaders = { +- "Content-Type": "application/json", +- "x-request-id": "sdfsdfsdfdsf" +- }; +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file" +- }; +- var callback = sinon.spy(); +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- +- await sleep(); +- server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", +- [ +- 200, +- dummyResonseHeaders, ++ dummyResponseHeaders, + JSON.stringify(uploadSuccessResponseObj) + ] + ); + server.respond(); + await sleep(); + +- expect(callback.calledOnce).to.be.true; +- +- var callBackArguments = callback.args[0]; +- expect(callBackArguments.length).to.be.eq(2); +- var callbackResult = callBackArguments[1]; +- +- expect(callbackResult).to.be.deep.equal(uploadSuccessResponseObj); +- expect(callbackResult.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); +- expect(callbackResult.$ResponseMetadata.statusCode).to.be.deep.equal(200); ++ const response = await uploadPromise; ++ expect(response.$ResponseMetadata.headers).to.deep.equal(dummyResponseHeaders); ++ expect(response.$ResponseMetadata.statusCode).to.equal(200); ++ expect(response.$ResponseMetadata.requestId).to.equal("sdfsdfsdfdsf"); + }); + + it('Undefined fields should not be sent', async function () { +@@ -1063,10 +999,7 @@ describe("File upload", function () { + customMetadata: undefined + }; + +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); +@@ -1088,246 +1021,243 @@ describe("File upload", function () { + expect(arg.get('overwriteTags')).to.be.equal(undefined); + expect(arg.get('overwriteCustomMetadata')).to.be.equal(undefined); + expect(arg.get('customMetadata')).to.be.equal(undefined); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it("With pre and post transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- successUploadResponse(); +- await sleep(); +- +- var arg = server.requests[0].requestBody; +- +- expect(arg.get("file")).to.be.equal("test_file"); +- expect(arg.get("fileName")).to.be.equal("test_file_name"); +- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); +- expect(arg.get("useUniqueFileName")).to.be.equal("false"); +- expect(arg.get("publicKey")).to.be.equal("test_public_key"); +- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); +- +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ successUploadResponse(); ++ await sleep(); ++ ++ var arg = server.requests[0].requestBody; ++ ++ expect(arg.get("file")).to.be.equal("test_file"); ++ expect(arg.get("fileName")).to.be.equal("test_file_name"); ++ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); ++ expect(arg.get("useUniqueFileName")).to.be.equal("false"); ++ expect(arg.get("publicKey")).to.be.equal("test_public_key"); ++ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); ++ ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it("With pre transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: { pre: "w-100" }, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- successUploadResponse(); +- await sleep(); +- +- var arg = server.requests[0].requestBody; +- +- expect(arg.get("file")).to.be.equal("test_file"); +- expect(arg.get("fileName")).to.be.equal("test_file_name"); +- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); +- expect(arg.get("useUniqueFileName")).to.be.equal("false"); +- expect(arg.get("publicKey")).to.be.equal("test_public_key"); +- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); +- +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { pre: "w-100" }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ successUploadResponse(); ++ await sleep(); ++ ++ var arg = server.requests[0].requestBody; ++ ++ expect(arg.get("file")).to.be.equal("test_file"); ++ expect(arg.get("fileName")).to.be.equal("test_file_name"); ++ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); ++ expect(arg.get("useUniqueFileName")).to.be.equal("false"); ++ expect(arg.get("publicKey")).to.be.equal("test_public_key"); ++ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); ++ ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + + it("With post transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: { post: [{ type: "transformation", value: "w-100" }] }, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- successUploadResponse(); +- await sleep(); +- +- var arg = server.requests[0].requestBody; +- +- expect(arg.get("file")).to.be.equal("test_file"); +- expect(arg.get("fileName")).to.be.equal("test_file_name"); +- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); +- expect(arg.get("useUniqueFileName")).to.be.equal("false"); +- expect(arg.get("publicKey")).to.be.equal("test_public_key"); +- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); +- +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { post: [{ type: "transformation", value: "w-100" }] }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ successUploadResponse(); ++ await sleep(); ++ ++ var arg = server.requests[0].requestBody; ++ ++ expect(arg.get("file")).to.be.equal("test_file"); ++ expect(arg.get("fileName")).to.be.equal("test_file_name"); ++ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); ++ expect(arg.get("useUniqueFileName")).to.be.equal("false"); ++ expect(arg.get("publicKey")).to.be.equal("test_public_key"); ++ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); ++ ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); + }); + +- it("Should return error for an invalid transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: {}, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- var errRes = { +- help: "", +- message: "Invalid transformation parameter. Please include at least pre, post, or both.", +- }; +- errorUploadResponse(500, errRes); +- await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ it("Server 5xx without message", async function () { ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ var errRes = { ++ help: "" ++ }; ++ errorUploadResponse(500, errRes); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitServerError).to.be.true; ++ expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); ++ } + }); + + it("Should return error for an invalid pre transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: { pre: "" }, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- var errRes = { +- help: "", +- message: "Invalid pre transformation parameter.", +- }; +- errorUploadResponse(500, errRes); +- await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { pre: "" }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ var errRes = { ++ help: "", ++ message: "Invalid pre transformation parameter.", ++ }; ++ errorUploadResponse(500, errRes); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid pre transformation parameter."); ++ } + }); + + it("Should return error for an invalid post transformation of type abs", async function () { +- const fileOptions = { +- ...securityParameters, +- 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); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- var errRes = { +- help: "", +- message: "Invalid post transformation parameter.", +- }; +- errorUploadResponse(500, errRes); +- await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { post: [{ type: "abs", value: "" }] }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ var errRes = { ++ help: "", ++ message: "Invalid post transformation parameter.", ++ }; ++ errorUploadResponse(500, errRes); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid post transformation parameter."); ++ } + }); + + it("Should return error for an invalid post transformation of type transformation", async function () { +- const fileOptions = { +- ...securityParameters, +- 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); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- var errRes = { +- help: "", +- message: "Invalid post transformation parameter.", +- }; +- errorUploadResponse(500, errRes); +- await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { post: [{ type: "transformation", value: "" }] }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ var errRes = { ++ help: "", ++ message: "Invalid post transformation parameter.", ++ }; ++ errorUploadResponse(500, errRes); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid post transformation parameter."); ++ } + }); + + it("Should return error for an invalid post transformation if it's not an array", async function () { +- const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- transformation: { post: {} }, +- }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- +- expect(server.requests.length).to.be.equal(1); +- await sleep(); +- var errRes = { +- help: "", +- message: "Invalid post transformation parameter.", +- }; +- errorUploadResponse(500, errRes); +- await sleep(); +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, errRes, null); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ transformation: { post: {} }, ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ await sleep(); ++ var errRes = { ++ help: "", ++ message: "Invalid post transformation parameter.", ++ }; ++ errorUploadResponse(500, errRes); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid post transformation parameter."); ++ } + }); + + it("With checks option", async function () { + const fileOptions = { +- ...securityParameters, +- fileName: "test_file_name", +- file: "test_file", +- responseFields: "tags, customCoordinates, isPrivateFile, metadata", +- useUniqueFileName: false, +- checks: "'request.folder' : '/'", ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ responseFields: "tags, customCoordinates, isPrivateFile, metadata", ++ useUniqueFileName: false, ++ checks: "'request.folder' : '/'", + }; +- var callback = sinon.spy(); +- +- imagekit.upload(fileOptions, callback); +- ++ const uploadPromise = upload(fileOptions); + expect(server.requests.length).to.be.equal(1); + await sleep(); + successUploadResponse(); + await sleep(); +- ++ + var arg = server.requests[0].requestBody; + expect(arg.get("file")).to.be.equal("test_file"); + expect(arg.get("fileName")).to.be.equal("test_file_name"); +@@ -1335,8 +1265,130 @@ describe("File upload", function () { + expect(arg.get("useUniqueFileName")).to.be.equal("false"); + expect(arg.get("publicKey")).to.be.equal("test_public_key"); + expect(arg.get('checks')).to.be.equal("'request.folder' : '/'"); +- +- expect(callback.calledOnce).to.be.true; +- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); ++ ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); ++ }); ++ ++ it('onProgress callback is triggered during upload', async function () { ++ const progressSpy = sinon.spy(); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ onProgress: progressSpy ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ server.requests[0].uploadProgress({ lengthComputable: true, loaded: 50, total: 100 }); ++ ++ await sleep(); ++ expect(progressSpy.calledOnce).to.be.true; ++ successUploadResponse(); ++ await sleep(); ++ expect(progressSpy.calledTwice).to.be.true; // final progress ++ const response = await uploadPromise; ++ expect(response).to.be.deep.equal(uploadSuccessResponseObj); ++ }); ++ ++ it('Abort signal aborts the upload', async function () { ++ const abortController = new AbortController(); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ abortSignal: abortController.signal ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ abortController.abort(); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitAbortError).to.be.true; ++ expect(ex.reason.name).to.be.equal("AbortError"); ++ } ++ }); ++ ++ it('Abort signal aborts the upload with reason', async function () { ++ const abortController = new AbortController(); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ abortSignal: abortController.signal ++ }; ++ const uploadPromise = upload(fileOptions); ++ expect(server.requests.length).to.be.equal(1); ++ abortController.abort("abort reason"); ++ await sleep(); ++ try { ++ await uploadPromise; ++ throw new Error('Should have thrown error'); ++ } catch (ex) { ++ expect(ex instanceof ImageKitAbortError).to.be.true; ++ expect(ex.reason).to.be.equal("abort reason"); ++ } ++ }); ++ ++ it("Already aborted signal should abort upload immediately", async function () { ++ const abortController = new AbortController(); ++ // Abort the signal before calling upload ++ abortController.abort(); ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ abortSignal: abortController.signal ++ }; ++ try { ++ await upload(fileOptions); ++ throw new Error("Should have thrown error"); ++ } catch (ex) { ++ expect(ex instanceof ImageKitAbortError).to.be.true; ++ expect(ex.reason && ex.reason.name).to.be.equal("AbortError"); ++ } ++ }); ++ ++ it("Error during upload 4xx with invalid JSON response", async function () { ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file" ++ }; ++ const uploadPromise = upload(fileOptions); ++ // errorUploadResponse(400, `{sd`); ++ server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", ++ [ ++ 400, ++ { "Content-Type": "application/json" }, ++ "sdf" ++ ] ++ ); ++ server.respond(); ++ try { ++ await uploadPromise; ++ throw new Error("Should have thrown error"); ++ } catch (ex) { ++ expect(ex).to.be.instanceOf(SyntaxError); ++ } ++ }); ++ ++ it("Should return error for an invalid transformation object in upload", async function () { ++ const fileOptions = { ++ ...securityParameters, ++ fileName: "test_file_name", ++ file: "test_file", ++ transformation: 123 ++ }; ++ try { ++ await upload(fileOptions); ++ throw new Error("Should have thrown error"); ++ } catch (ex) { ++ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; ++ expect(ex.message).to.be.equal("Invalid transformation parameter. Please include at least pre, post, or both."); ++ } + }); + }); +diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js +index cb81ef2..739958c 100644 +--- a/test/url-generation/basic.js ++++ b/test/url-generation/basic.js +@@ -1,36 +1,51 @@ + const chai = require("chai"); +-const pkg = require("../../package.json"); +-global.FormData = require('formdata-node'); + const expect = chai.expect; +-const initializationParams = require("../data").initializationParams +-import ImageKit from "../../src/index"; ++import { buildSrc } from "../../src/index"; + + describe("URL generation", function () { ++ it('should return an empty string when src is not provided', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query" ++ }); + +- var imagekit = new ImageKit(initializationParams); ++ expect(url).equal(""); ++ }); + +- it('should return an empty string when neither path nor src is provided', function () { +- const url = imagekit.url({}); ++ it('should return an empty string when src is /', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/" ++ }); + +- expect(url).equal(""); ++ expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); + }); + +- it('should return an empty string for an invalid src URL', function () { +- const url = imagekit.url({ src: "/" }); ++ it('should return an empty string when src is invalid', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "https://" ++ }); + + expect(url).equal(""); + }); + +- it('should generate a valid URL when a path is provided without transformation', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg" ++ it('should generate a valid URL when src is provided without transformation', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg" + }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); + }); + + it('should generate a valid URL when a src is provided without transformation', function () { +- const url = imagekit.url({ ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", + src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" + }); + +@@ -38,951 +53,1235 @@ describe("URL generation", function () { + }); + + it('should generate a valid URL when undefined transformation parameters are provided with path', function () { +- const url = imagekit.url({ +- path: "/test_path_alt.jpg", ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/test_path_alt.jpg", + transformation: undefined, +- transformationPosition: undefined, +- src: undefined, ++ transformationPosition: "query" + }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); + }); + + it("By default transformationPosition should be query", function () { +- var imagekitNew = new ImageKit({ +- publicKey: "test_public_key", ++ const url = buildSrc({ + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", +- }); +- const url = imagekitNew.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }, { +- rotation: 90 +- }] ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ }, ++ { ++ rotation: 90 ++ } ++ ] + }); + expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90"); + }); + + it('should generate the URL without sdk version', function () { +- const ik = new ImageKit(initializationParams) +- +- const url = ik.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ], ++ transformationPosition: "path" + }); + + 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 a valid path and transformation', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ it('should generate the correct URL with a valid src and transformation', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/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`); ++ // Now transformed URL goes into query since transformationPosition is "query". ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL when the provided path contains multiple leading slashes', function () { +- const url = 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`); ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "///test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ] ++ }); + ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL when the urlEndpoint is overridden', function () { +- const url = imagekit.url({ ++ const url = buildSrc({ ++ // We do not override urlEndpoint here + 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`); ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ] ++ }); + ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=h-300,w-400`); + }); + +- it('should generate the correct URL with transformationPosition as query parameter when path is provided', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", ++ it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/test_path.jpg", + transformationPosition: "query", +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ] + }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL with a valid src parameter and transformation', function () { +- const url = imagekit.url({ ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", + src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ] + }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); + }); + + it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { +- const url = imagekit.url({ ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", + transformationPosition: "query", +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ transformation: [ ++ { ++ height: "300", ++ width: "400" ++ } ++ ] + }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); + }); + + it('should merge query parameters correctly in the generated URL', function () { +- const url = imagekit.url({ ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", + src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", + queryParameters: { t2: "v2", t3: "v3" }, +- transformation: [{ +- "height": "300", +- "width": "400" +- }] ++ 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,w-400`); + }); + +- + it('should generate the correct URL with chained transformations', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }, { +- "rt": "90" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/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`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90`); + }); + +- + it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400" +- }, { +- "rndm_trnsf": "abcd" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/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`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rndm_trnsf-abcd`); + }); + + it('should generate the correct URL when overlay image transformation is provided', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400", +- "raw": "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400", ++ raw: "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" ++ } ++ ] ++ }); + +- expect(url).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`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end`); + }); + + it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { +- const url = 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" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400", ++ raw: "l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end" ++ } ++ ] ++ }); + +- expect(url).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`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end`); + }); + + it('should generate the correct URL when border transformation is applied', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "height": "300", +- "width": "400", +- border: "20_FF0000" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: "300", ++ width: "400", ++ border: "20_FF0000" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,b-20_FF0000/test_path.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,b-20_FF0000`); + }); + + it('should generate the correct URL when transformation has empty key and value', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "": "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ "": "" ++ } ++ ] ++ }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); + }); + +- /** +- * Provided to provide support to a new transform without sdk update +- */ + it('should generate the correct URL when an undefined transform is provided', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- "undefined-transform": "true" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ "undefined-transform": "true" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:undefined-transform-true/test_path.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=undefined-transform-true`); + }); + + it('should generate the correct URL when transformation key has an empty value', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- defaultImage: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ defaultImage: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-/test_path.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=di-`); + }); + + it('should generate the correct URL when transformation key has \'-\' as its value', function () { +- const url = imagekit.url({ +- path: "/test_path.jpg", +- transformation: [{ +- contrastStretch: "-" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ contrastStretch: "-" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-contrast/test_path.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=e-contrast`); + }); + + it('should skip transformation parameters that are undefined or null', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- quality: undefined, +- contrastStretch: null +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ quality: undefined, ++ contrastStretch: null ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); + }); + + it('should skip transformation parameters that are false', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- contrastStretch: false +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ contrastStretch: false ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); + }); + + it('should include only the key when transformation value is an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- shadow: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ shadow: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow`); + }); + + it('should include both key and value when transformation parameter value is provided', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- shadow: "bl-15_st-40_x-10_y-N5" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ shadow: "bl-15_st-40_x-10_y-N5" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5`); + }); + + it('should generate the correct URL when trim transformation is set to true as a boolean', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- trim: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ trim: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); + }); + + it('should generate the correct URL when trim transformation is set to true as a string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- defaultImage: "/test_path.jpg", +- trim: "true" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ defaultImage: "/test_path.jpg", ++ trim: "true" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); + }); + + it('should generate the correct URL for AI background removal when set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackground: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackground: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); + }); + + it('should generate the correct URL for AI background removal when \'true\' is provided as a string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackground: "true" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackground: "true" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); + }); + + it('should not apply AI background removal when value is not true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackground: "false" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackground: "false" ++ } ++ ] ++ }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); + }); + + it('should generate the correct URL for external AI background removal when set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackgroundExternal: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackgroundExternal: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); + }); + + it('should generate the correct URL for external AI background removal when \'true\' is provided as a string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackgroundExternal: "true" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackgroundExternal: "true" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); + }); + + it('should not apply external AI background removal when value is not true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiRemoveBackgroundExternal: "false" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiRemoveBackgroundExternal: "false" ++ } ++ ] ++ }); + + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); + }); + + it('should generate the correct URL when gradient transformation is provided as a string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- gradient: "ld-top_from-green_to-00FF0010_sp-1" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ gradient: "ld-top_from-green_to-00FF0010_sp-1" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient-ld-top_from-green_to-00FF0010_sp-1/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1`); + }); + + it('should generate the correct URL when gradient transformation is provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- gradient: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ gradient: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); + }); + + it('should generate the correct URL when gradient transformation is set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- gradient: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ gradient: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); + }); + + it('should generate the correct URL when AI drop shadow transformation is set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiDropShadow: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiDropShadow: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); + }); + + it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiDropShadow: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiDropShadow: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); + }); + + it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aiDropShadow: "az-45" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aiDropShadow: "az-45" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow-az-45/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-az-45`); + }); + + it('should generate the correct URL when shadow transformation is set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- shadow: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ shadow: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); + }); + + it('should generate the correct URL when shadow transformation is provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- shadow: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ shadow: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); + }); + + it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- shadow: "bl-15_st-40_x-10_y-N5" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ shadow: "bl-15_st-40_x-10_y-N5" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow-bl-15_st-40_x-10_y-N5`); + }); + + it('should generate the correct URL when sharpen transformation is set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- sharpen: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ sharpen: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); + }); + + it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- sharpen: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ sharpen: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); + }); + + it('should generate the correct URL when sharpen transformation is provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- sharpen: 10 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ sharpen: 10 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen-10/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen-10`); + }); + + it('should generate the correct URL when unsharpMask transformation is set to true', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- unsharpMask: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ unsharpMask: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); + }); + + it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- unsharpMask: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ unsharpMask: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); + }); + + it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- unsharpMask: "2-2-0.8-0.024" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ unsharpMask: "2-2-0.8-0.024" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm-2-2-0.8-0.024/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm-2-2-0.8-0.024`); + }); + + it('should generate the correct URL for trim transformation when set to true (boolean)', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- trim: true +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ trim: true ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); + }); + + it('should generate the correct URL for trim transformation when provided as an empty string', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- trim: "" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ trim: "" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); + }); + + it('should generate the correct URL for trim transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- trim: 5 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ trim: 5 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-5/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-5`); + }); + + // Width parameter tests + it('should generate the correct URL for width transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- width: 400 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ width: 400 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); + }); + + it('should generate the correct URL for width transformation when provided with a string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- width: "400" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ width: "400" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); + }); + + it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- width: "iw_div_2" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ width: "iw_div_2" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-iw_div_2/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-iw_div_2`); + }); + + // Height parameter tests + it('should generate the correct URL for height transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- height: 300 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ height: 300 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); + }); + + it('should generate the correct URL for height transformation when provided with a string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- height: "300" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ height: "300" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); + }); + + it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- height: "ih_mul_0.5" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ height: "ih_mul_0.5" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-ih_mul_0.5/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-ih_mul_0.5`); + }); + + // AspectRatio parameter tests + it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aspectRatio: "4:3" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aspectRatio: "4:3" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4:3/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3`); + }); + + it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aspectRatio: "4_3" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aspectRatio: "4_3" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4_3/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4_3`); + }); + + it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- aspectRatio: "iar_div_2" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ aspectRatio: "iar_div_2" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-iar_div_2/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-iar_div_2`); + }); + + // Background parameter tests + it('should generate the correct URL for background transformation when provided with a solid color', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- background: "FF0000" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ background: "FF0000" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-FF0000/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-FF0000`); + }); + + it('should generate the correct URL for background transformation when provided with the blurred option', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- background: "blurred" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ background: "blurred" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-blurred/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-blurred`); + }); + + it('should generate the correct URL for background transformation when provided with the genfill option', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- background: "genfill" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ background: "genfill" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-genfill/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-genfill`); + }); + + // Crop parameter tests + it('should generate the correct URL for crop transformation when provided with force value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- crop: "force" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ crop: "force" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-force/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-force`); + }); + + it('should generate the correct URL for crop transformation when provided with at_max value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- crop: "at_max" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ crop: "at_max" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-at_max/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-at_max`); + }); + + // CropMode parameter tests + it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- cropMode: "pad_resize" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ cropMode: "pad_resize" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-pad_resize/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-pad_resize`); + }); + + it('should generate the correct URL for cropMode transformation when provided with extract value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- cropMode: "extract" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ cropMode: "extract" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-extract/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-extract`); + }); + + // Focus parameter tests + it('should generate the correct URL for focus transformation when provided with a string value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- focus: "center" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ focus: "center" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-center/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-center`); + }); + + it('should generate the correct URL for focus transformation when face detection is specified', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- focus: "face" +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ focus: "face" ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-face/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-face`); + }); + + // Quality parameter test + it('should generate the correct URL for quality transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- quality: 80 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ quality: 80 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:q-80/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80`); + }); + + // Coordinate parameters tests + it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- x: 10 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ x: 10 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:x-10/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=x-10`); + }); + + it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- y: 20 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ y: 20 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:y-20/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=y-20`); + }); + + it('should generate the correct URL for xCenter transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- xCenter: 30 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ xCenter: 30 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:xc-30/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=xc-30`); + }); + + it('should generate the correct URL for yCenter transformation when provided with a number value', function () { +- const url = imagekit.url({ +- path: "/test_path1.jpg", +- transformation: [{ +- yCenter: 40 +- }] +- }) ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path1.jpg", ++ transformation: [ ++ { ++ yCenter: 40 ++ } ++ ] ++ }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:yc-40/test_path1.jpg`); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=yc-40`); + }); + +- // This is done just to test how SDK constructs URL, the actual transformation is not valid. + it('Including deprecated properties', 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, +- sharpen: 10, +- unsharpMask: "2-2-0.8-0.024", +- contrastStretch: true, +- grayscale: true, +- shadow: 'bl-15_st-40_x-10_y-N5', +- gradient: 'from-red_to-white', +- original: true, +- raw: "h-200,w-300,l-image,i-logo.png,l-end" +- }] +- }) +- +- expect(url).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,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); +- }); +- +- // This is done just to test how SDK constructs URL, the actual transformation is not valid +- it('should generate the correct URL when comprehensive transformations, including video and AI transformations, are applied', 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, +- x: 10, +- y: 20, +- xCenter: 30, +- yCenter: 40, +- flip: "h", +- opacity: 0.8, +- zoom: 2, +- // Video transformations +- videoCodec: "h264", +- audioCodec: "aac", +- startOffset: 5, +- endOffset: 15, +- duration: 10, +- streamingResolutions: ["1440", "1080"], +- // AI transformations +- grayscale: true, +- aiUpscale: true, +- aiRetouch: true, +- aiVariation: true, +- aiDropShadow: true, +- aiChangeBackground: "prompt-car", +- aiRemoveBackground: true, +- contrastStretch: true, +- shadow: 'bl-15_st-40_x-10_y-N5', +- sharpen: 10, +- unsharpMask: "2-2-0.8-0.024", +- gradient: 'from-red_to-white', +- original: true, +- page: "2_4", +- raw: "h-200,w-300,l-image,i-logo.png,l-end" +- }] +- }) +- +- expect(url).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,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); ++ // This is just testing how the SDK constructs the URL, not actual valid transformations. ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: 300, ++ width: 400, ++ aspectRatio: '4-3', ++ quality: 40, ++ crop: 'force', ++ cropMode: 'extract', ++ focus: 'left', ++ format: 'jpeg', ++ radius: 50, ++ 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/", ++ dpr: 3, ++ sharpen: 10, ++ unsharpMask: "2-2-0.8-0.024", ++ contrastStretch: true, ++ grayscale: true, ++ shadow: "bl-15_st-40_x-10_y-N5", ++ gradient: "from-red_to-white", ++ original: true, ++ raw: "h-200,w-300,l-image,i-logo.png,l-end" ++ } ++ ] ++ }); ++ ++ expect(url).equal( ++ `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end` ++ ); ++ }); ++ ++ it('should generate the correct URL with many transformations, including video and AI transforms', function () { ++ // Example test with comprehensive transformations ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ transformationPosition: "query", ++ src: "/test_path.jpg", ++ transformation: [ ++ { ++ height: 300, ++ width: 400, ++ aspectRatio: '4-3', ++ quality: 40, ++ crop: 'force', ++ cropMode: 'extract', ++ focus: 'left', ++ format: 'jpeg', ++ radius: 50, ++ 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/", ++ dpr: 3, ++ x: 10, ++ y: 20, ++ xCenter: 30, ++ yCenter: 40, ++ flip: "h", ++ opacity: 0.8, ++ zoom: 2, ++ // Video transformations ++ videoCodec: "h264", ++ audioCodec: "aac", ++ startOffset: 5, ++ endOffset: 15, ++ duration: 10, ++ streamingResolutions: ["1440", "1080"], ++ // AI transformations ++ grayscale: true, ++ aiUpscale: true, ++ aiRetouch: true, ++ aiVariation: true, ++ aiDropShadow: true, ++ aiChangeBackground: "prompt-car", ++ aiRemoveBackground: true, ++ contrastStretch: true, ++ shadow: "bl-15_st-40_x-10_y-N5", ++ sharpen: 10, ++ unsharpMask: "2-2-0.8-0.024", ++ gradient: "from-red_to-white", ++ original: true, ++ page: "2_4", ++ raw: "h-200,w-300,l-image,i-logo.png,l-end" ++ } ++ ] ++ }); ++ ++ expect(url).equal( ++ `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end` ++ ); + }); + }); +diff --git a/test/url-generation/buildtransformationString.js b/test/url-generation/buildtransformationString.js +new file mode 100644 +index 0000000..f116b2e +--- /dev/null ++++ b/test/url-generation/buildtransformationString.js +@@ -0,0 +1,26 @@ ++const { buildTransformationString } = require("../../src/index"); ++const { expect } = require('chai'); ++ ++describe('buildTransformationString', function () { ++ it('should return an empty string when no transformations are provided', function () { ++ const result = buildTransformationString([{}]); ++ expect(result).to.equal(''); ++ }); ++ ++ it('should generate a transformation string for width only', function () { ++ const result = buildTransformationString([{ width: 300 }]); ++ expect(result).to.equal('w-300'); ++ }); ++ ++ it('should generate a transformation string for multiple transformations', function () { ++ const result = buildTransformationString([ ++ { ++ overlay: { ++ type: 'text', ++ text: 'Hello', ++ } ++ } ++ ]); ++ expect(result).to.equal('l-text,i-Hello,l-end'); ++ }); ++}); +diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js +index 52f67b4..0ade645 100644 +--- a/test/url-generation/overlay.js ++++ b/test/url-generation/overlay.js +@@ -1,14 +1,15 @@ + const chai = require("chai"); + const expect = chai.expect; +-const initializationParams = require("../data").initializationParams; +-import ImageKit from "../../src/index"; ++import { buildSrc } from "../../src/index"; + import { safeBtoa } from "../../src/utils/transformation"; ++ + describe("Overlay Transformation Test Cases", function () { +- const imagekit = new ImageKit(initializationParams); + + it('Ignore invalid values if text is missing', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "text" +@@ -18,9 +19,24 @@ describe("Overlay Transformation Test Cases", function () { + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + +- it('Ignore invalid values if input', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ it('Ignore if type is missing', function () { ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", ++ transformation: [{ ++ overlay: { ++ } ++ }] ++ }); ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); ++ }); ++ ++ it('Ignore invalid values if input (image)', function () { ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image" +@@ -30,9 +46,11 @@ describe("Overlay Transformation Test Cases", function () { + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + +- it('Ignore invalid values if input', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ it('Ignore invalid values if input (video)', function () { ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "video" +@@ -42,9 +60,11 @@ describe("Overlay Transformation Test Cases", function () { + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + +- it('Ignore invalid values if input', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ it('Ignore invalid values if input (subtitle)', function () { ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "subtitle" +@@ -54,9 +74,11 @@ describe("Overlay Transformation Test Cases", function () { + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); + }); + +- it('Ignore invalid values if color is missing', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ it('Ignore invalid values if color is missing (solidColor)', function () { ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "solidColor" +@@ -67,8 +89,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it('Text overlay generates correct URL with encoded overlay text', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -80,8 +104,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it('Image overlay generates correct URL with input logo.png', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", +@@ -93,8 +119,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { +- const url = imagekit.url({ +- path: "/base-video.mp4", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-video.mp4", + transformation: [{ + overlay: { + type: "video", +@@ -106,8 +134,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it("Subtitle overlay generates correct URL with input subtitle.srt", function () { +- const url = imagekit.url({ +- path: "/base-video.mp4", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-video.mp4", + transformation: [{ + overlay: { + type: "subtitle", +@@ -119,8 +149,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it("Solid color overlay generates correct URL with background color FF0000", function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "solidColor", +@@ -132,8 +164,10 @@ describe("Overlay Transformation Test Cases", function () { + }); + + it('Combined overlay transformations generate correct URL including nested overlays', function () { +- const url = imagekit.url({ +- path: "/base-image.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", ++ src: "/base-image.jpg", + transformation: [ + { + // Text overlay +@@ -197,7 +231,7 @@ describe("Overlay Transformation Test Cases", function () { + } + }, + { +- // Video overlay. Just for url generation testing, you can't overlay a video on an image. ++ // Video overlay. Just for URL generation testing, you can't actually overlay a video on an image. + overlay: { + type: "video", + input: "play-pause-loop.mp4", +@@ -220,7 +254,7 @@ describe("Overlay Transformation Test Cases", function () { + } + }, + { +- // Subtitle overlay. Just for url generation testing, you can't overlay a subtitle on an image. ++ // Subtitle overlay. Just for URL generation testing, you can't actually overlay a subtitle on an image. + overlay: { + type: "subtitle", + input: "subtitle.srt", +@@ -268,20 +302,17 @@ describe("Overlay Transformation Test Cases", function () { + ] + }); + +- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) ++ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`); + }); + }); + +- + describe("Overlay encoding test cases", function () { +- const imagekit = new ImageKit({ +- ...initializationParams, +- urlEndpoint: "https://ik.imagekit.io/demo", // Using real url to test correctness quickly by clicking link +- }); +- + it('Nested simple path, should use i instead of ie, handle slash properly', function () { +- const url = imagekit.url({ +- path: "/medium_cafe_B1iTdD0C.jpg", ++ const url = buildSrc({ ++ // Using a different endpoint here, as we are checking for /demo ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "image", +@@ -293,8 +324,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Nested non-simple path, should use ie instead of i', function () { +- const url = imagekit.url({ +- path: "/medium_cafe_B1iTdD0C.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "image", +@@ -306,8 +339,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Simple text overlay, should use i instead of ie', function () { +- const url = imagekit.url({ +- path: "/medium_cafe_B1iTdD0C.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -319,8 +354,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { +- const url = imagekit.url({ +- path: "/medium_cafe_B1iTdD0C.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -332,8 +369,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Non simple text overlay, should use ie instead of i', function () { +- const url = imagekit.url({ +- path: "/medium_cafe_B1iTdD0C.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/medium_cafe_B1iTdD0C.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -345,8 +384,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Text overlay with explicit plain encoding', function () { +- const url = imagekit.url({ +- path: "/sample.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -359,8 +400,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Text overlay with explicit base64 encoding', function () { +- const url = imagekit.url({ +- path: "/sample.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", +@@ -373,8 +416,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Image overlay with explicit plain encoding', function () { +- const url = imagekit.url({ +- path: "/sample.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.jpg", + transformation: [{ + overlay: { + type: "image", +@@ -387,8 +432,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Image overlay with explicit base64 encoding', function () { +- const url = imagekit.url({ +- path: "/sample.jpg", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.jpg", + transformation: [{ + overlay: { + type: "image", +@@ -401,8 +448,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Video overlay with explicit base64 encoding', function () { +- const url = imagekit.url({ +- path: "/sample.mp4", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.mp4", + transformation: [{ + overlay: { + type: "video", +@@ -415,8 +464,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Subtitle overlay with explicit plain encoding', function () { +- const url = imagekit.url({ +- path: "/sample.mp4", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.mp4", + transformation: [{ + overlay: { + type: "subtitle", +@@ -429,8 +480,10 @@ describe("Overlay encoding test cases", function () { + }); + + it('Subtitle overlay with explicit base64 encoding', function () { +- const url = imagekit.url({ +- path: "/sample.mp4", ++ const url = buildSrc({ ++ transformationPosition: "path", ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.mp4", + transformation: [{ + overlay: { + type: "subtitle", +@@ -443,8 +496,9 @@ describe("Overlay encoding test cases", function () { + }); + + it("Avoid double encoding when transformation string is in query params", function () { +- const url = imagekit.url({ +- path: "/sample.jpg", ++ const url = buildSrc({ ++ urlEndpoint: "https://ik.imagekit.io/demo", ++ src: "/sample.jpg", + transformation: [{ + overlay: { + type: "text", From 0090d927c5b9d85fdde0566a561127639a59230e Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:25:20 +0530 Subject: [PATCH 146/166] No code changes detected; skipping commit. --- diff.diff | 6713 ----------------------------------------------------- 1 file changed, 6713 deletions(-) delete mode 100644 diff.diff diff --git a/diff.diff b/diff.diff deleted file mode 100644 index 2a56085..0000000 --- a/diff.diff +++ /dev/null @@ -1,6713 +0,0 @@ -diff --git a/.babelrc b/.babelrc -index e1f3fc1..081c354 100644 ---- a/.babelrc -+++ b/.babelrc -@@ -1,5 +1,8 @@ - { -- "plugins": ["@babel/plugin-proposal-class-properties"], -+ "plugins": [ -+ ["@babel/plugin-transform-class-properties", { "loose": true }], -+ "@babel/plugin-transform-optional-chaining" -+], - "presets": [ - "@babel/preset-typescript", - [ -diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml -index b328240..102ba51 100644 ---- a/.github/workflows/nodejs.yml -+++ b/.github/workflows/nodejs.yml -@@ -13,7 +13,7 @@ jobs: - - strategy: - matrix: -- node-version: [12.x] -+ node-version: [20.x] - - steps: - - uses: actions/checkout@v1 -@@ -26,6 +26,7 @@ jobs: - npm install - npm run build - npm run test -- npm run report-coverage - env: - CI: true -+ - name: Upload coverage to Codecov -+ uses: codecov/codecov-action@v3 -diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml -index 0159c40..44a47a4 100644 ---- a/.github/workflows/npmpublish.yml -+++ b/.github/workflows/npmpublish.yml -@@ -12,7 +12,7 @@ jobs: - - strategy: - matrix: -- node-version: [12.x] -+ node-version: [20.x] - - steps: - - uses: actions/checkout@v1 -@@ -35,14 +35,24 @@ jobs: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: -- node-version: 12 -+ node-version: 20 - registry-url: https://registry.npmjs.org/ - - name: NPM Publish - run: | - npm install - npm run build - npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN -- npm publish -+ # print the NPM user name for validation -+ npm whoami -+ VERSION=$(node -p "require('./package.json').version" ) -+ # Only publish stable versions to the latest tag -+ if [[ "$VERSION" =~ ^[^-]+$ ]]; then -+ NPM_TAG="latest" -+ else -+ NPM_TAG="beta" -+ fi -+ echo "Publishing $VERSION with $NPM_TAG tag." -+ npm publish --tag $NPM_TAG --access public - env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} - CI: true -\ No newline at end of file -diff --git a/.gitignore b/.gitignore -index 48d5826..9361b82 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -7,4 +7,5 @@ dist - .nyc_output - coverage.lcov - coverage --out-tsc -\ No newline at end of file -+out-tsc -+docs -\ No newline at end of file -diff --git a/.mocharc.json b/.mocharc.json -index b758d0a..b06f6a6 100644 ---- a/.mocharc.json -+++ b/.mocharc.json -@@ -1,6 +1,6 @@ - { - "coverage": true, -- "require": ["esm", "./babel-register.js"], -+ "require": ["./babel-register.js"], - "exit": true, - "timeout": "40000" --} -\ No newline at end of file -+} -diff --git a/.npmignore b/.npmignore -new file mode 100644 -index 0000000..483a9c4 ---- /dev/null -+++ b/.npmignore -@@ -0,0 +1 @@ -+package-lock.json -\ No newline at end of file -diff --git a/README.md b/README.md -index d522861..dfe276c 100644 ---- a/README.md -+++ b/README.md -@@ -2,540 +2,37 @@ - - # ImageKit.io JavaScript SDK - --![gzip size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=gzip&label=gzip) --![brotli size](https://img.badgesize.io/https://unpkg.com/imagekit-javascript/dist/imagekit.min.js?compression=brotli&label=brotli) -+![gzip size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=gzip&label=gzip) -+![brotli size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=brotli&label=brotli) - ![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) --[![npm version](https://img.shields.io/npm/v/imagekit-javascript)](https://www.npmjs.com/package/imagekit-javascript) -+[![npm version](https://img.shields.io/npm/v/@imagekit/javascript)](https://www.npmjs.com/package/@imagekit/javascript) - [![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) - [![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) - --A lightweight JavaScript SDK for generating image and video URLs with transformations, and for uploading files directly from the browser to ImageKit. This SDK is intended for use in the browser only. For Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). -+This lightweight, dependency-free JavaScript SDK is designed specifically for browser use. It provides utility functions to generate image and video `src` URLs using [ImageKit transformations](https://imagekit.io/docs/transformations) and to upload files to the ImageKit media library. - --## Table of Contents --- [Installation](#installation) --- [Initialization](#initialization) --- [URL Generation](#url-generation) -- - [Basic URL Generation](#basic-url-generation) -- - [Advanced URL Generation Examples](#advanced-url-generation-examples) -- - [Chained Transformations](#chained-transformations) -- - [Overlays and Effects](#overlays-and-effects) -- - [AI and Advanced Transformations](#ai-and-advanced-transformations) -- - [Arithmetic Expressions in Transformations](#arithmetic-expressions-in-transformations) -- - [Supported Transformations](#supported-transformations) -- - [Handling Unsupported Transformations](#handling-unsupported-transformations) --- [File Upload](#file-upload) -- - [Basic Upload Example](#basic-upload-example) -- - [Promise-based Upload Example](#promise-based-upload-example) --- [Test Examples](#test-examples) --- [Changelog](#changelog) -+For server-side applications with Node.js, please refer to our official [Node.js SDK](https://github.com/imagekit-developer/imagekit-nodejs). - - ## Installation - --### Using npm --Install the SDK via npm: --```bash --npm install imagekit-javascript --save --# or --yarn add imagekit-javascript --``` -- --Then import ImageKit: --```js --import ImageKit from "imagekit-javascript"; --// or with CommonJS: --const ImageKit = require("imagekit-javascript"); --``` -- --### Using CDN --You can also use the global CDN: -- --Download a specific version: --``` --https://unpkg.com/imagekit-javascript@1.3.0/dist/imagekit.min.js --``` --Or for the latest version, remove the version number (don't use in production as it may break your code if a new major version is released): --``` --https://unpkg.com/imagekit-javascript/dist/imagekit.min.js --``` -- --And include it in your HTML: --```html -- --``` -- --## Initialization --To use the SDK, initialize it with your ImageKit URL endpoint. You can get the URL endpoint [here](https://imagekit.io/dashboard/url-endpoints) and your public API key from the [developer section](https://imagekit.io/dashboard/developer/api-keys): -- --```js --var imagekit = new ImageKit({ -- urlEndpoint: "https://ik.imagekit.io/your_imagekit_id", // Required -- transformationPosition: "query", // Optional, defaults to "query" -- publicKey: "your_public_api_key", // Optional, required only for client-side file uploads --}); --``` -- --> Note: Never include your private API key in client-side code. The SDK will throw an error if you do. -- --### Initialization Options -- --| Option | Description | Example | --| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | --| urlEndpoint | Required. Your ImageKit URL endpoint or custom domain. | `urlEndpoint: "https://ik.imagekit.io/your_id"` | --| transformationPosition | Optional. Specifies whether transformations are added as URL path segments (`path`) or query parameters (`query`). The default is `query`, which allows you to perform wildcard purges and remove all generated transformations from the CDN cache. | `transformationPosition: "query"` | --| publicKey | Optional. Your public API key for client-side uploads. | `publicKey: "your_public_api_key"` | -- -- --## URL Generation -- --The SDK’s `.url()` method enables you to generate optimized image and video URLs with a variety of transformations. -- --The method accepts an object with the following parameters: -- --| Option | Description | Example | --| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | --| path | The relative path of the image. Either `src` or `path` must be provided. | `"/path/to/image.jpg"` | --| src | The full URL of an image already mapped to ImageKit. Either `src` or `path` must be provided. | `"https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg"` | --| transformation | An array of objects specifying the transformations to be applied in the URL. Each object contains key-value pairs representing transformation parameters. See [supported transformations](#supported-transformations). | `[ { width: 300, height: 400 } ]` | --| queryParameters | Additional query parameters to be appended to the URL. | `{ v: 1 }` | -- --Optionally, you can include `transformationPosition` and `urlEndpoint` in the object to override the initialization settings for a specific `.url()` call. -- --### Basic URL Generation -- --*A simple height and width transformation:* -- --```js --var imageURL = imagekit.url({ -- path: "/default-image.jpg", -- urlEndpoint: "https://ik.imagekit.io/your_imagekit_id/endpoint/", -- transformation: [{ -- height: 300, -- width: 400 -- }] --}); --``` -- --*Result Example:* --``` --https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,w-400 --``` -- --SDK automatically generates the URL based on the provided parameters. The generated URL includes the base URL, path, and transformation parameters. -- --### Advanced URL Generation Examples -- --#### Chained Transformations --Apply multiple transformations by passing an array: --```js --var imageURL = imagekit.url({ -- path: "/default-image.jpg", -- transformation: [{ -- height: 300, -- width: 400 -- }, { -- rotation: 90 -- }], --}); --``` -- --*Result Example:* --``` --https://ik.imagekit.io/your_imagekit_id/default-image.jpg?tr=h-300,w-400:rt-90 --``` -- --#### Overlays and Effects --*Text Overlay Example:* --```js --var imageURL = imagekit.url({ -- src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", -- transformation: [{ -- width: 400, -- height: 300, -- overlay: { -- text: "Imagekit", -- fontSize: 50, -- color: "red", -- position: { -- x: 10, -- y: 20 -- } -- } -- }] --}); --``` -- --*Image Overlay Example:* -- --```js --var imageURL = imagekit.url({ -- src: "https://ik.imagekit.io/your_imagekit_id/default-image.jpg", -- transformation: [{ -- width: 400, -- height: 300, -- overlay: { -- type: "image", -- input: "logo.png", -- transformation: [{ -- width: 100, -- border: "10_CDDC39" -- }], -- position: { -- focus: "top_left" -- } -- } -- }] --}); --``` -- --*Video Overlay Example:* -+You can install the SDK in your project using npm or yarn. - --```js --var videoOverlayURL = imagekit.url({ -- src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", -- transformation: [{ -- overlay: { -- type: "video", -- input: "overlay-video.mp4", -- position: { -- x: "10", -- y: "20" -- }, -- timing: { -- start: 5, -- duration: 10 -- } -- } -- }] --}); --``` -- --*Subtitle Overlay Example:* -- --```js --var subtitleOverlayURL = imagekit.url({ -- src: "https://ik.imagekit.io/your_imagekit_id/base-video.mp4", -- transformation: [{ -- overlay: { -- type: "subtitle", -- input: "subtitle.vtt", -- transformation: [{ -- fontSize: 16, -- fontFamily: "Arial" -- }], -- position: { -- focus: "bottom" -- }, -- timing: { -- start: 0, -- duration: 5 -- } -- } -- }] --}); --``` -- --*Solid Color Overlay Example:* --```js --var solidColorOverlayURL = imagekit.url({ -- src: "https://ik.imagekit.io/your_imagekit_id/base-image.jpg", -- transformation: [{ -- overlay: { -- type: "solidColor", -- color: "FF0000", -- transformation: [{ -- width: 100, -- height: 50, -- alpha: 5 -- }], -- position: { x: 20, y: 20 } -- } -- }] --}); --``` -- --##### Overlay Options -- --ImageKit supports various overlay types, including text, image, video, subtitle, and solid color overlays. Each overlay type has specific configuration options to customize the overlay appearance and behavior. To learn more about how overlays work, refer to the [ImageKit documentation](https://imagekit.io/docs/transformations#overlay-using-layers). -- --The table below outlines the available overlay configuration options: -- --| Option | Description | Example | --| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | --| type | Specifies the type of overlay. Supported values: `text`, `image`, `video`, `subtitle`, `solidColor`. | `type: "text"` | --| text | (For text overlays) The text content to display. | `text: "ImageKit"` | --| input | (For image, video, or subtitle overlays) Relative path to the overlay asset. | `input: "logo.png"` or `input: "overlay-video.mp4"` | --| color | (For solidColor overlays) RGB/RGBA hex code or color name for the overlay color. | `color: "FF0000"` | --| encoding | Accepted values: `auto`, `plain`, `base64`. [Check this](#encoding-options) for more details. | `encoding: "auto"` | --| transformation | An array of transformation objects to style the overlay.
- [Text Overlay Transformations](#text-overlay-transformations)
- [Subtitle Overlay Transformations](#subtitle-overlay-transformations)
- Image and video overlays support most [transformations](#supported-transformations).
See [ImageKit docs](https://imagekit.io/docs/transformations#overlay-using-layers) for more details. | `transformation: [{ fontSize: 50 }]` | --| position | Sets the overlay’s position relative to the base asset. Accepts an object with `x`, `y`, or `focus`. The `focus` value can be one of: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. | `position: { x: 10, y: 20 }` or `position: { focus: "center" }` | --| timing | (For video base) Specifies when the overlay appears using `start`, `duration`, and `end` (in seconds); if both `duration` and `end` are set, `duration` is ignored. | `timing: { start: 5, duration: 10 }` | -- --##### Encoding Options -- --Overlay encoding options define how the overlay input is converted for URL construction. When set to `auto`, the SDK automatically determines whether to use plain text or Base64 encoding based on the input content. -- --For text overlays: --- If `auto` is used, the SDK checks the text overlay input: if it is URL-safe, it uses the format `i-{input}` (plain text); otherwise, it applies Base64 encoding with the format `ie-{base64_encoded_input}`. --- You can force a specific method by setting encoding to `plain` (always use `i-{input}`) or `base64` (always use `ie-{base64}`). --- Note: In all cases, the text is percent-encoded to ensure URL safety. -- --For image, video, and subtitle overlays: --- The input path is processed by removing any leading/trailing slashes and replacing inner slashes with `@@` when `plain` is used. --- Similarly, if `auto` is used, the SDK determines whether to apply plain text or Base64 encoding based on the characters present. --- For explicit behavior, use `plain` or `base64` to enforce the desired encoding. -- --Use `auto` for most cases to let the SDK optimize encoding, and use `plain` or `base64` when a specific encoding method is required. -- --##### Solid Color Overlay Transformations -- --| Option | Description | Example | --| ------ | ---------------------------------------------------------------------------------------------------------------------------------- | --------------- | --| width | Specifies the width of the solid color overlay block (in pixels or as an arithmetic expression). | `width: 100` | --| height | Specifies the height of the solid color overlay block (in pixels or as an arithmetic expression). | `height: 50` | --| radius | Specifies the corner radius of the solid color overlay block or shape. Can be a number or `"max"` for circular/oval shapes. | `radius: "max"` | --| alpha | Specifies the transparency level of the solid color overlay. Supports integers from 1 (most transparent) to 9 (least transparent). | `alpha: 5` | -- --##### Text Overlay Transformations -- --| Option | Description | Example | --| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | --| width | Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions are supported (e.g., `bw_mul_0.2` or `bh_div_2`). | `width: 400` | --| fontSize | Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. | `fontSize: 50` | --| fontFamily | Specifies the font family of the overlaid text. Choose from the supported fonts or provide a custom font. | `fontFamily: "Arial"` | --| fontColor | Specifies the font color of the overlaid text. Accepts an RGB hex code, an RGBA code, or a standard color name. | `fontColor: "FF0000"` | --| innerAlignment | Specifies the inner alignment of the text when it doesn’t occupy the full width. Supported values: `left`, `right`, `center`. | `innerAlignment: "center"` | --| padding | Specifies the padding around the text overlay. Can be a single integer or multiple values separated by underscores; arithmetic expressions are accepted. | `padding: 10` | --| alpha | Specifies the transparency level of the text overlay. Accepts an integer between `1` and `9`. | `alpha: 5` | --| typography | Specifies the typography style of the text. Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. | `typography: "b"` | --| background | Specifies the background color of the text overlay. Accepts an RGB hex code, an RGBA code, or a color name. | `background: "red"` | --| radius | Specifies the corner radius of the text overlay. Accepts a numeric value or `max` for circular/oval shape. | `radius: "max"` | --| rotation | Specifies the rotation angle of the text overlay. Accepts a numeric value for clockwise rotation or a string prefixed with `N` for counterclockwise rotation. | `rotation: 90` | --| flip | Specifies the flip option for the text overlay. Supported values: `h`, `v`, `h_v`, `v_h`. | `flip: "h"` | --| lineHeight | Specifies the line height for multi-line text. Accepts a numeric value or an arithmetic expression. | `lineHeight: 1.5` | -- --##### Subtitle Overlay Transformations -- --| Option | Description | Example | --| ----------- | --------------------------------------------------------------------------------------------------------- | ----------------------- | --| background | Specifies the subtitle background color using a standard color name, RGB color code, or RGBA color code. | `background: "blue"` | --| fontSize | Sets the font size of subtitle text. | `fontSize: 16` | --| fontFamily | Sets the font family of subtitle text. | `fontFamily: "Arial"` | --| color | Specifies the font color of subtitle text using standard color name, RGB, or RGBA color code. | `color: "FF0000"` | --| typography | Sets the typography style of subtitle text. Supported values: `b`, `i`, `b_i`. | `typography: "b"` | --| fontOutline | Specifies the font outline for subtitles. Requires an outline width and color separated by an underscore. | `fontOutline: "2_blue"` | --| fontShadow | Specifies the font shadow for subtitles. Requires shadow color and indent separated by an underscore. | `fontShadow: "blue_2"` | -- --#### AI and Advanced Transformations --*Background Removal:* --```js --var imageURL = imagekit.url({ -- path: "/sample-image.jpg", -- transformation: [{ -- aiRemoveBackground: true -- }] --}); --``` --*Upscaling:* --```js --var upscaledURL = imagekit.url({ -- path: "/sample-image.jpg", -- transformation: [{ -- aiUpscale: true -- }] --}); --``` --*Drop Shadow:* --```js --var dropShadowURL = imagekit.url({ -- path: "/sample-image.jpg", -- transformation: [{ -- aiDropShadow: "az-45" -- }] --}); --``` -- --#### Arithmetic Expressions in Transformations --```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" -- }] --}); --``` -- --### Supported Transformations -- --The SDK gives a name to each transformation parameter (e.g. `height` maps to `h`, `width` maps to `w`). If the property does not match any of the following supported options, it is added as is. -- --If you want to generate transformations without any modifications, use the `raw` parameter. -- --Check ImageKit [transformation documentation](https://imagekit.io/docs/transformations) for more details. -- --| Transformation Name | URL Parameter | --| -------------------------- | ---------------------------------------------------------------------------------------------- | --| width | w | --| height | h | --| aspectRatio | ar | --| quality | q | --| aiRemoveBackground | e-bgremove (ImageKit powered) | --| aiRemoveBackgroundExternal | e-removedotbg (Using third party) | --| aiUpscale | e-upscale | --| aiRetouch | e-retouch | --| aiVariation | e-genvar | --| aiDropShadow | e-dropshadow | --| aiChangeBackground | e-changebg | --| crop | c | --| cropMode | cm | --| x | x | --| y | y | --| xCenter | xc | --| yCenter | yc | --| focus | fo | --| format | f | --| radius | r | --| background | bg | --| border | b | --| rotation | rt | --| blur | bl | --| named | n | --| dpr | dpr | --| progressive | pr | --| lossless | lo | --| trim | t | --| metadata | md | --| colorProfile | cp | --| defaultImage | di | --| original | orig | --| videoCodec | vc | --| audioCodec | ac | --| grayscale | e-grayscale | --| contrastStretch | e-contrast | --| shadow | e-shadow | --| sharpen | e-sharpen | --| unsharpMask | e-usm | --| gradient | e-gradient | --| flip | fl | --| opacity | o | --| zoom | z | --| page | pg | --| startOffset | so | --| endOffset | eo | --| duration | du | --| streamingResolutions | sr | --| overlay | Generates the correct layer syntax for image, video, text, subtitle, and solid color overlays. | --| raw | The string provided in raw will be added in the URL as is. | -- --### Handling Unsupported Transformations -- --If you specify a transformation parameter that is not explicitly supported by the SDK, it is added “as-is” in the generated URL. This provides flexibility for using new or custom transformations without waiting for an SDK update. -- --For example: --```js --var imageURL = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- newparam: "cool" -- }] --}); --// Generated URL: https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=newparam-cool -+```bash -+npm install @imagekit/javascript - ``` - --## File Upload -- --The SDK offers a simple interface via the `.upload()` method to upload files to the ImageKit Media Library. This method requires the following: --- **file** (mandatory) --- **fileName** (mandatory) --- Security parameters: **signature**, **token**, and **expire** -- --Before invoking the upload, generate the necessary security parameters as per the [ImageKit Upload API documentation](https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload). -+## TypeScript support - --### Upload Options --| Option | Description | Example | --| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | --| file | The file content to be uploaded. Accepts binary, base64 string, or URL. | `file: fileInput.files[0]` | --| fileName | The name to assign to the uploaded file. Supports alphanumeric characters, dot, underscore, and dash. | `fileName: "myImage.jpg"` | --| signature | HMAC-SHA1 digest computed using the private API key. Must be calculated on the server side. | `signature: "generated_signature"` | --| token | A unique token to prevent duplicate upload retries. Typically a V4 UUID or similar unique string. | `token: "unique_upload_token"` | --| expire | Unix timestamp (in seconds) indicating the signature expiry time (should be within 1 hour). | `expire: 1616161616` | --| useUniqueFileName | Boolean flag to automatically generate a unique filename if set to true. Defaults to true. | `useUniqueFileName: true` | --| folder | The folder path where the file will be uploaded. Automatically creates nested folders if they don’t exist. | `folder: "/images/uploads"` | --| isPrivateFile | Boolean to mark the file as private, restricting access to the original file URL. Defaults to false. | `isPrivateFile: false` | --| tags | Tags to associate with the file. Can be a comma-separated string or an array of tags. | `tags: "summer,holiday"` or `tags: ["summer","holiday"]` | --| customCoordinates | Specifies an area of interest in the image formatted as `x,y,width,height`. | `customCoordinates: "10,10,100,100"` | --| responseFields | Comma-separated list of fields to include in the upload response. | `responseFields: "tags,customCoordinates"` | --| extensions | Array of extension objects for additional image processing. | `extensions: [{ name: "auto-tagging" }]` | --| webhookUrl | URL to which the final status of extension processing will be sent. | `webhookUrl: "https://example.com/webhook"` | --| overwriteFile | Boolean flag indicating whether to overwrite a file if it exists. Defaults to true. | `overwriteFile: true` | --| overwriteAITags | Boolean flag to remove AITags from a file if overwritten. Defaults to true. | `overwriteAITags: true` | --| overwriteTags | Boolean flag that determines if existing tags should be removed when new tags are not provided. Defaults to true when file is overwritten without tags. | `overwriteTags: true` | --| overwriteCustomMetadata | Boolean flag dictating if existing custom metadata should be removed when not provided. Defaults to true under similar conditions as tags. | `overwriteCustomMetadata: true` | --| customMetadata | Stringified JSON or an object containing custom metadata key-value pairs to associate with the file. | `customMetadata: {author: "John Doe"}` | --| transformation | Optional transformation object to apply during the upload process. It follows the same structure as in URL generation. | `transformation: { pre: "w-200,h-200", post: [...] }` | --| xhr | An optional XMLHttpRequest object provided to monitor upload progress. | `xhr: new XMLHttpRequest()` | --| checks | Optional string value for specifying server-side checks to run before file upload. | `checks: "file.size' < '1MB'"` | -+The SDK is written in TypeScript, offering first-class TypeScript support. Enjoy excellent type safety and IntelliSense in your IDE. You can use it in your TypeScript projects without any additional configuration. - --### Basic Upload Example - --Below is an HTML form example that uses a callback for handling the upload response: -- --```html --
-- -- --
-- -- --``` -- --### Promise-based Upload Example -- --You can also use promises for a cleaner asynchronous approach: --```js --imagekit.upload({ -- file: file.files[0], -- fileName: "abc1.jpg", -- token: 'generated_token', -- signature: 'generated_signature', -- expire: 'generated_expire' --}).then(result => { -- console.log(result); --}).catch(error => { -- console.error(error); --}); --``` -+To enable type checking in JavaScript projects, add `//@ts-check` at the top of your JavaScript files. This will activate type checking in your IDE. - --## Test Examples -+## Documentation - --For a quick demonstration of the SDK features, check the test suite: --- URL generation examples can be found in [basic.js](./test/url-generation/basic.js) and [overlay.js](./test/url-generation/overlay.js) files. --- File upload examples can be found in [test/upload.js](./test/upload.js). -+Refer to the ImageKit [official documentation](https://imagekit.io/docs/integration/javascript) for more details on how to use the SDK. - - ## Changelog - --For a detailed history of changes, please refer to [CHANGELOG.md](CHANGELOG.md). -\ No newline at end of file -+For a detailed history of changes, refer to [CHANGELOG.md](CHANGELOG.md). - -diff --git a/package.json b/package.json -index 2d9a978..b64f91a 100644 ---- a/package.json -+++ b/package.json -@@ -1,19 +1,21 @@ - { -- "name": "imagekit-javascript", -- "version": "4.0.1", -- "description": "Javascript SDK for using ImageKit.io in the browser", -+ "name": "@imagekit/javascript", -+ "version": "5.0.0", -+ "description": "ImageKit Javascript SDK", - "main": "dist/imagekit.cjs.js", - "module": "dist/imagekit.esm.js", - "browser": "dist/imagekit.min.js", - "unpkg": "dist/imagekit.min.js", -- "types": "dist/src/index.d.ts", -+ "types": "dist/index.d.ts", - "files": [ -- "dist", -- "src" -+ "dist" - ], - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.10.5", -+ "@babel/plugin-proposal-class-properties": "^7.18.6", -+ "@babel/plugin-proposal-optional-chaining": "^7.21.0", -+ "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/preset-env": "^7.10.4", - "@babel/preset-typescript": "^7.13.0", - "@babel/register": "^7.14.5", -@@ -24,8 +26,7 @@ - "@types/node": "^15.6.1", - "babel-plugin-transform-class-properties": "^6.24.1", - "chai": "^4.2.0", -- "codecov": "^3.8.0", -- "esm": "^3.2.25", -+ "codecov": "^3.8.3", - "formdata-node": "2.1.0", - "mocha": "^7.0.1", - "nyc": "^15.1.0", -@@ -43,9 +44,12 @@ - "dev": "rollup -c -w", - "export-types": "tsc", - "build": "rm -rf dist*;rollup -c && yarn export-types", -- "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha \"test/**/*.js\"", -- "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js", -- "report-coverage": "codecov" -+ "test": "NODE_ENV=test nyc ./node_modules/mocha/bin/mocha --require ./test/setup.js \"test/**/*.js\"", -+ "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" -+ }, -+ "publishConfig": { -+ "tag": "beta", -+ "access": "public" - }, - "repository": { - "type": "git", -@@ -54,14 +58,12 @@ - "keywords": [ - "imagekit", - "javascript", -- "sdk", -- "js", - "image", -+ "video", -+ "upload", - "optimization", - "transformation", - "resize", -- "upload", -- "video", - "overlay" - ], - "author": "ImageKit Developer", -@@ -69,6 +71,5 @@ - "bugs": { - "url": "https://github.com/imagekit-developer/imagekit-javascript/issues" - }, -- "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme", -- "dependencies": {} -+ "homepage": "https://github.com/imagekit-developer/imagekit-javascript#readme" - } -diff --git a/rollup.config.js b/rollup.config.js -index 747e961..95ae3de 100644 ---- a/rollup.config.js -+++ b/rollup.config.js -@@ -12,8 +12,7 @@ export default [ - output: { - name: "ImageKit", - file: pkg.browser, -- format: "umd", -- sourceMap: true, -+ format: "umd" - }, - plugins: [ - nodeResolve({ extensions: [".ts"] }), -@@ -34,8 +33,8 @@ export default [ - { - input: "src/index.ts", - output: [ -- { file: pkg.main, format: "cjs", exports: "default" }, -- { file: pkg.module, format: "es", exports: "default" }, -+ { file: pkg.main, format: "cjs", exports: "named" }, -+ { file: pkg.module, format: "es", exports: "named" }, - ], - plugins: [ - nodeResolve({ extensions: [".ts"] }), -diff --git a/samples/sample-app/views/index.pug b/samples/sample-app/views/index.pug -index 91d398f..4c53a49 100644 ---- a/samples/sample-app/views/index.pug -+++ b/samples/sample-app/views/index.pug -@@ -23,6 +23,7 @@ html - script(type='text/javascript' src="./imagekit.min.js") - script. - try { -+ window.controller = new AbortController(); - var imagekit = new ImageKit({ - publicKey: "!{publicKey}", - urlEndpoint: "!{urlEndpoint}", -@@ -51,9 +52,11 @@ html - var statusEl = document.getElementById("status"); - statusEl.innerHTML = "Uploading..."; - -+ - // Use this if you want to track upload progress - var customXHR = new XMLHttpRequest(); - customXHR.upload.addEventListener('progress', function (e) { -+ console.log("On progress event handler from customXHR"); - if (e.loaded <= fileSize) { - var percent = Math.round(e.loaded / fileSize * 100); - console.log(`Uploaded ${percent}%`); -@@ -94,6 +97,11 @@ html - token: securityParametersObj.token, - signature: securityParametersObj.signature, - expire: securityParametersObj.expire, -+ signal: window.controller.signal, -+ onProgress: function(e) { -+ console.log("On progress event handler from SDK"); -+ console.log(e.loaded); -+ }, - //- extensions: [ - //- { - //- name: "aws-auto-tagging", -@@ -102,6 +110,7 @@ html - //- } - //- ], - }, function(err, result) { -+ debugger; - if (err) { - statusEl.innerHTML = "Error uploading image. "+ err.message; - console.log(err) -diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts -index 06d8b51..4b394f9 100644 ---- a/src/constants/errorMessages.ts -+++ b/src/constants/errorMessages.ts -@@ -1,24 +1,14 @@ - export default { -- MANDATORY_INITIALIZATION_MISSING: { message: "Missing urlEndpoint during SDK initialization", help: "" }, -- INVALID_TRANSFORMATION_POSITION: { message: "Invalid transformationPosition parameter", help: "" }, -- PRIVATE_KEY_CLIENT_SIDE: { message: "privateKey should not be passed on the client side", help: "" }, -- 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: "" }, -- MISSING_AUTHENTICATION_ENDPOINT: { message: "Missing authentication endpoint for upload", help: "" }, -- MISSING_PUBLIC_KEY: { message: "Missing public key for upload", help: "" }, -- AUTH_ENDPOINT_TIMEOUT: { message: "The authenticationEndpoint you provided timed out in 60 seconds", help: "" }, -- AUTH_ENDPOINT_NETWORK_ERROR: { message: "Request to authenticationEndpoint failed due to network error", help: "" }, -- AUTH_INVALID_RESPONSE: { message: "Invalid response from authenticationEndpoint. The SDK expects a JSON response with three fields i.e. signature, token and expire.", help: "" }, -+ MISSING_UPLOAD_FILE_PARAMETER: { message: "Missing file parameter for upload" }, -+ MISSING_UPLOAD_FILENAME_PARAMETER: { message: "Missing fileName parameter for upload" }, -+ MISSING_PUBLIC_KEY: { message: "Missing public key for upload" }, - UPLOAD_ENDPOINT_NETWORK_ERROR: { -- message: "Request to ImageKit upload endpoint failed due to network error", -- help: "", -+ message: "Request to ImageKit upload endpoint failed due to network error" - }, -- INVALID_UPLOAD_OPTIONS: { message: "Invalid uploadOptions parameter", help: "" }, -- MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: ""}, -- MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: ""}, -- MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: ""}, -- 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: ""}, -+ MISSING_SIGNATURE: { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication." }, -+ MISSING_TOKEN: { message: "Missing token for upload. The SDK expects token, signature and expire for authentication." }, -+ MISSING_EXPIRE: { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication." }, -+ INVALID_TRANSFORMATION: { message: "Invalid transformation parameter. Please include at least pre, post, or both." }, -+ INVALID_PRE_TRANSFORMATION: { message: "Invalid pre transformation parameter." }, -+ INVALID_POST_TRANSFORMATION: { message: "Invalid post transformation parameter." } - }; -diff --git a/src/index.ts b/src/index.ts -index 6594fa7..4b047e1 100644 ---- a/src/index.ts -+++ b/src/index.ts -@@ -1,91 +1,13 @@ --import { version } from "../package.json"; --import errorMessages from "./constants/errorMessages"; --import { ImageKitOptions, UploadOptions, UploadResponse, UrlOptions } from "./interfaces"; --import IKResponse from "./interfaces/IKResponse"; --import { upload } from "./upload/index"; --import respond from "./utils/respond"; --import { url } from "./url/index"; --import transformationUtils from "./utils/transformation"; -- --function mandatoryParametersAvailable(options: ImageKitOptions) { -- return options.urlEndpoint; --} -- --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); -- }); -- } -- }; -+import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; -+import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; -+import { buildSrc, buildTransformationString } from "./url"; -+ -+export { buildSrc, buildTransformationString, upload, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; -+export type { -+ Transformation, -+ SrcOptions, -+ UploadOptions, -+ UploadResponse - }; - --class ImageKit { -- options: ImageKitOptions = { -- publicKey: "", -- urlEndpoint: "", -- transformationPosition: transformationUtils.getDefault(), -- }; -- -- constructor(opts: ImageKitOptions) { -- this.options = { ...this.options, ...(opts || {}) }; -- if (!mandatoryParametersAvailable(this.options)) { -- throw errorMessages.MANDATORY_INITIALIZATION_MISSING; -- } -- -- if (!transformationUtils.validParameters(this.options)) { -- throw errorMessages.INVALID_TRANSFORMATION_POSITION; -- } -- } -- -- /** -- * A utility function to generate asset URL. It applies the specified transformations and other parameters to the URL. -- */ -- url(urlOptions: UrlOptions): string { -- return url(urlOptions, this.options); -- } -- -- /** -- * For uploading files directly from the browser to ImageKit.io. -- * -- * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#how-to-implement-client-side-file-upload} -- */ -- upload(uploadOptions: UploadOptions, options?: Partial): Promise> -- upload(uploadOptions: UploadOptions, callback: (err: Error | null, response: IKResponse | null) => void, options?: Partial): void; -- upload(uploadOptions: UploadOptions, callbackOrOptions?: ((err: Error | null, response: IKResponse | null) => void) | Partial, options?: Partial): void | Promise> { -- let callback; -- if (typeof callbackOrOptions === 'function') { -- callback = callbackOrOptions; -- } else { -- options = callbackOrOptions || {}; -- } -- if (!uploadOptions || typeof uploadOptions !== "object") { -- return respond(true, errorMessages.INVALID_UPLOAD_OPTIONS, callback); -- } -- var mergedOptions = { -- ...this.options, -- ...options, -- }; -- const { xhr: userProvidedXHR } = uploadOptions || {}; -- delete uploadOptions.xhr; -- const xhr = userProvidedXHR || new XMLHttpRequest(); -- return promisify>(this, upload)(xhr, uploadOptions, mergedOptions, callback); -- } --} - --export default ImageKit; -diff --git a/src/interfaces/IKResponse.ts b/src/interfaces/IKResponse.ts -deleted file mode 100644 -index a53ca4f..0000000 ---- a/src/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/src/interfaces/ImageKitOptions.ts b/src/interfaces/ImageKitOptions.ts -deleted file mode 100644 -index 6f8b78f..0000000 ---- a/src/interfaces/ImageKitOptions.ts -+++ /dev/null -@@ -1,7 +0,0 @@ --import { TransformationPosition } from "."; -- --export interface ImageKitOptions { -- urlEndpoint: string; -- publicKey?: string; -- transformationPosition?: TransformationPosition; --} -diff --git a/src/interfaces/SrcOptions.ts b/src/interfaces/SrcOptions.ts -new file mode 100644 -index 0000000..4b0187e ---- /dev/null -+++ b/src/interfaces/SrcOptions.ts -@@ -0,0 +1,35 @@ -+import { Transformation } from "./Transformation"; -+import { TransformationPosition } from "."; -+ -+export interface SrcOptions { -+ /** -+ * Accepts a relative or absolute path of the resource. If a relative path is provided, it is appended to the `urlEndpoint`. -+ * If an absolute path is provided, `urlEndpoint` is ignored. -+ */ -+ src: string; -+ -+ /** -+ * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). -+ */ -+ urlEndpoint: string; -+ -+ /** -+ * An array of objects specifying the transformations to be applied in the URL. If more than one transformation is specified, they are applied in the order they are specified as chained transformations. -+ * -+ * {@link https://imagekit.io/docs/transformations#chained-transformations} -+ */ -+ transformation?: Array; -+ -+ /** -+ * These are additional query parameters that you want to add to the final URL. -+ * They can be any query parameters and not necessarily related to ImageKit. -+ * This is especially useful if you want to add a versioning parameter to your URLs. -+ */ -+ queryParameters?: { [key: string]: string | number }; -+ -+ /** -+ * By default, the transformation string is added as a query parameter in the URL, e.g., `?tr=w-100,h-100`. -+ * If you want to add the transformation string in the path of the URL, set this to `path`. -+ */ -+ transformationPosition?: TransformationPosition; -+} -diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts -index adbe8dd..b45be9f 100644 ---- a/src/interfaces/Transformation.ts -+++ b/src/interfaces/Transformation.ts -@@ -4,394 +4,395 @@ export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440 - - /** - * The SDK provides easy-to-use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. -- * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, you can use the `raw` parameter to pass the transformation string directly. -+ * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, -+ * You can use the `raw` parameter to pass the transformation string directly. - * -- * {@link https://imagekit.io/docs/transformations|Transformations Documentation} -+ * [Transformations Documentation](https://imagekit.io/docs/transformations) - */ - export interface Transformation { - /** -- * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage -- * (e.g., `0.4` represents 40% of the original width). You can also supply arithmetic expressions (e.g., `iw_div_2`). -+ * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.4` represents 40% of the original width). -+ * You can also supply arithmetic expressions (e.g., `iw_div_2`). - * -- * Width transformation - {@link https://imagekit.io/docs/image-resize-and-crop#width---w|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#width---w|Videos} -+ * Width transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) | [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) - */ - width?: number | string; - - /** -- * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage -- * (e.g., `0.5` represents 50% of the original height). You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). -+ * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.5` represents 50% of the original height). -+ * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). - * -- * Height transformation - {@link https://imagekit.io/docs/image-resize-and-crop#height---h|Images} | {@link https://imagekit.io/docs/video-resize-and-crop#height---h|Videos} -+ * Height transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) | [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) - */ - height?: number | string; - - /** -- * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). -+ * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). - * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. - * -- * {@link https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar|Image Resize and Crop - Aspect Ratio} -+ * [Image Resize and Crop - Aspect Ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar) - */ - aspectRatio?: number | string; - - /** -- * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. -+ * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. - * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#solid-color-background|Effects and Enhancements - Solid Color Background} -+ * [Effects and Enhancements - Solid Color Background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background) - * - * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#blurred-background|Effects and Enhancements - Blurred Background} -+ * [Effects and Enhancements - Blurred Background](https://imagekit.io/docs/effects-and-enhancements#blurred-background) - * - * - Expand the image boundaries using generative fill: `genfill`. Not supported inside overlay. Optionally, control the background scene by passing a text prompt: - * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. - * -- * {@link https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill|AI Transformations - Generative Fill Background} -+ * [AI Transformations - Generative Fill Background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill) - */ - background?: string; - - /** -- * Adds a border to the output media. Accepts a string in the format `_` -+ * Adds a border to the output media. Accepts a string in the format `_` - * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#border---b|Effects and Enhancements - Border} -+ * [Effects and Enhancements - Border](https://imagekit.io/docs/effects-and-enhancements#border---b) - */ - border?: string; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} -+ * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) - */ - crop?: "force" | "at_max" | "at_max_enlarge" | "at_least" | "maintain_ratio"; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus|Image Resize and Crop - Crop Modes} -+ * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) - */ - cropMode?: "pad_resize" | "extract" | "pad_extract"; - - /** - * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio (DPR) calculation. - * -- * {@link https://imagekit.io/docs/image-resize-and-crop#dpr---dpr|Image Resize and Crop - DPR} -+ * [Image Resize and Crop - DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr) - */ - dpr?: number - - /** -- * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. -+ * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. - * -- * {@link https://imagekit.io/docs/image-resize-and-crop#focus---fo|Image Resize and Crop - Focus} -+ * [Image Resize and Crop - Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo) - */ - focus?: string; - - /** -- * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. -+ * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. - * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. - * -- * {@link https://imagekit.io/docs/image-optimization#quality---q|Image Optimization - Quality} -+ * [Image Optimization - Quality](https://imagekit.io/docs/image-optimization#quality---q) - */ - quality?: number; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} -+ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) - */ - x?: number | string; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} -+ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) - */ - xCenter?: number | string; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} -+ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) - */ - y?: number | string; - - /** -- * {@link https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates|Image Resize and Crop - Focus Using Cropped Image Coordinates} -+ * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) - */ - yCenter?: number | string; - - /** -- * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. -+ * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. - * You can also pass `orig` for images to return the original format. - * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. - * -- * {@link https://imagekit.io/docs/image-optimization#format---f|Image Optimization - Format} & {@link https://imagekit.io/docs/video-optimization#format---f|Video Optimization - Format} -+ * [Image Optimization - Format](https://imagekit.io/docs/image-optimization#format---f) & [Video Optimization - Format](https://imagekit.io/docs/video-optimization#format---f) - */ - format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; - - /** -- * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. -+ * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. - * -- * {@link https://imagekit.io/docs/video-optimization#video-codec---vc|Video Optimization - Video Codec} -+ * [Video Optimization - Video Codec](https://imagekit.io/docs/video-optimization#video-codec---vc) - */ - videoCodec?: "h264" | "vp9" | "av1" | "none"; - - /** -- * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. -+ * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. - * -- * {@link https://imagekit.io/docs/video-optimization#audio-codec---ac|Video Optimization - Audio Codec} -+ * [Video Optimization - Audio Codec](https://imagekit.io/docs/video-optimization#audio-codec---ac) - */ - audioCodec?: "aac" | "opus" | "none"; - - /** -- * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. -+ * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#radius---r|Effects and Enhancements - Radius} -+ * [Effects and Enhancements - Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r) - */ - radius?: number | "max"; - - /** -- * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation -+ * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation - * or `auto` to use the orientation specified in the image's EXIF data. - * For videos, only the following values are supported: 0, 90, 180, 270, or 360. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#rotate---rt|Effects and Enhancements - Rotate} -+ * [Effects and Enhancements - Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt) - */ - rotation?: number | string; - - /** -- * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. -+ * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#blur---bl|Effects and Enhancements - Blur} -+ * [Effects and Enhancements - Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl) - */ - blur?: number; - - /** -- * {@link https://imagekit.io/docs/transformations#named-transformations|Transformations - Named Transformations} -+ * [Transformations - Named Transformations](https://imagekit.io/docs/transformations#named-transformations) - */ - named?: string; - - /** -- * Specifies a fallback image if the resource is not found, e.g., a URL or file path. -+ * Specifies a fallback image if the resource is not found, e.g., a URL or file path. - * -- * {@link https://imagekit.io/docs/image-transformation#default-image---di|Image Transformation - Default Image} -+ * [Image Transformation - Default Image](https://imagekit.io/docs/image-transformation#default-image---di) - */ - defaultImage?: string; - - /** -- * Flips or mirrors an image either horizontally, vertically, or both. -+ * Flips or mirrors an image either horizontally, vertically, or both. - * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#flip---fl|Effects and Enhancements - Flip} -+ * [Effects and Enhancements - Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl) - */ - flip?: "h" | "v" | "h_v" | "v_h"; - - /** -- * If set to true, serves the original file without applying any transformations. -+ * If set to true, serves the original file without applying any transformations. - * -- * {@link https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true|Core Delivery Features - Deliver Original File As Is} -+ * [Core Delivery Features - Deliver Original File As Is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true) - */ - original?: boolean; - - /** -- * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. -+ * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Arithmetic expressions are also supported. - * -- * {@link https://imagekit.io/docs/trim-videos#start-offset---so|Trim Videos - Start Offset} -+ * [Trim Videos - Start Offset](https://imagekit.io/docs/trim-videos#start-offset---so) - */ - startOffset?: number | string; - - /** -- * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. -+ * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to define a time window. Arithmetic expressions are supported. - * -- * {@link https://imagekit.io/docs/trim-videos#end-offset---eo|Trim Videos - End Offset} -+ * [Trim Videos - End Offset](https://imagekit.io/docs/trim-videos#end-offset---eo) - */ - endOffset?: number | string; - - /** -- * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. -+ * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. - * -- * {@link https://imagekit.io/docs/trim-videos#duration---du|Trim Videos - Duration} -+ * [Trim Videos - Duration](https://imagekit.io/docs/trim-videos#duration---du) - */ - duration?: number | string; - - /** -- * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. -+ * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. - * -- * {@link https://imagekit.io/docs/adaptive-bitrate-streaming|Adaptive Bitrate Streaming} -+ * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) - */ - streamingResolutions?: StreamingResolution[]; - - /** -- * Enables a grayscale effect for images. -+ * Enables a grayscale effect for images. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale|Effects and Enhancements - Grayscale} -+ * [Effects and Enhancements - Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale) - */ - grayscale?: true; - - /** -- * Upscales images beyond their original dimensions using AI. Not supported inside overlay. -+ * Upscales images beyond their original dimensions using AI. Not supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#upscale-e-upscale|AI Transformations - Upscale} -+ * [AI Transformations - Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale) - */ - aiUpscale?: true - - /** -- * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. -+ * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#retouch-e-retouch|AI Transformations - Retouch} -+ * [AI Transformations - Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch) - */ - aiRetouch?: true - - /** -- * Generates a variation of an image using AI. This produces a new image with slight variations from the original, -+ * Generates a variation of an image using AI. This produces a new image with slight variations from the original, - * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar|AI Transformations - Generate Variations} -+ * [AI Transformations - Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar) - */ - aiVariation?: true - - /** -- * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. -+ * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. - * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). - * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. - * Supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow|AI Transformations - Drop Shadow} -+ * [AI Transformations - Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow) - */ - aiDropShadow?: true | string - - /** -- * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, -+ * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, - * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. - * Not supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#change-background-e-changebg|AI Transformations - Change Background} -+ * [AI Transformations - Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg) - */ - aiChangeBackground?: string; - - /** -- * Applies ImageKit’s in-house background removal. -+ * Applies ImageKit’s in-house background removal. - * Supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove|AI Transformations - Background Removal} -+ * [AI Transformations - Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove) - */ - aiRemoveBackground?: true - - /** -- * Uses third-party background removal. -+ * Uses third-party background removal. - * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. - * Supported inside overlay. - * -- * {@link https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg|AI Transformations - External Background Removal} -+ * [AI Transformations - External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg) - */ - aiRemoveBackgroundExternal?: true - - /** -- * Automatically enhances the contrast of an image (contrast stretch). -+ * Automatically enhances the contrast of an image (contrast stretch). - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast|Effects and Enhancements - Contrast Stretch} -+ * [Effects and Enhancements - Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast) - */ - contrastStretch?: true - - /** -- * Adds a shadow beneath solid objects in an image with a transparent background. -+ * Adds a shadow beneath solid objects in an image with a transparent background. - * For AI-based drop shadows, refer to aiDropShadow. - * Pass `true` for a default shadow, or provide a string for a custom shadow. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow|Effects and Enhancements - Shadow} -+ * [Effects and Enhancements - Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow) - */ - shadow?: true | string - - /** -- * Sharpens the input image, highlighting edges and finer details. -+ * Sharpens the input image, highlighting edges and finer details. - * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen|Effects and Enhancements - Sharpen} -+ * [Effects and Enhancements - Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen) - */ - sharpen?: true | number - - /** -- * Applies Unsharp Masking (USM), an image sharpening technique. -+ * Applies Unsharp Masking (USM), an image sharpening technique. - * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm|Effects and Enhancements - Unsharp Mask} -+ * [Effects and Enhancements - Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm) - */ - unsharpMask?: true | string; - - /** -- * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. -+ * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient|Effects and Enhancements - Gradient} -+ * [Effects and Enhancements - Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient) - */ - gradient?: true | string; - - /** -- * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, -+ * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, - * pixelated version of the full image, which gradually improves to provide a faster perceived load time. - * -- * {@link https://imagekit.io/docs/image-optimization#progressive-image---pr|Image Optimization - Progressive Image} -+ * [Image Optimization - Progressive Image](https://imagekit.io/docs/image-optimization#progressive-image---pr) - */ - progressive?: boolean; - - /** -- * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. -+ * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. - * -- * {@link https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo|Image Optimization - Lossless Compression} -+ * [Image Optimization - Lossless Compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo) - */ - lossless?: boolean - - /** -- * Indicates whether the output image should retain the original color profile. -+ * Indicates whether the output image should retain the original color profile. - * -- * {@link https://imagekit.io/docs/image-optimization#color-profile---cp|Image Optimization - Color Profile} -+ * [Image Optimization - Color Profile](https://imagekit.io/docs/image-optimization#color-profile---cp) - */ - colorProfile?: boolean; - - /** -- * By default, ImageKit removes all metadata during automatic image compression. -+ * By default, ImageKit removes all metadata during automatic image compression. - * Set this to true to preserve metadata. - * -- * {@link https://imagekit.io/docs/image-optimization#image-metadata---md|Image Optimization - Image Metadata} -+ * [Image Optimization - Image Metadata](https://imagekit.io/docs/image-optimization#image-metadata---md) - */ - metadata?: boolean; - - /** -- * Specifies the opacity level of the output image. -+ * Specifies the opacity level of the output image. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#opacity---o|Effects and Enhancements - Opacity} -+ * [Effects and Enhancements - Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o) - */ - opacity?: number; - - /** -- * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, -+ * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, - * leaving only the central object in the output image. - * -- * {@link https://imagekit.io/docs/effects-and-enhancements#trim-edges---t|Effects and Enhancements - Trim Edges} -+ * [Effects and Enhancements - Trim Edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t) - */ - trim?: true | number; - - /** -- * Accepts a numeric value that determines how much to zoom in or out of the cropped area. -+ * Accepts a numeric value that determines how much to zoom in or out of the cropped area. - * It should be used in conjunction with fo-face or fo-. - * -- * {@link https://imagekit.io/docs/image-resize-and-crop#zoom---z|Image Resize and Crop - Zoom} -+ * [Image Resize and Crop - Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z) - */ - zoom?: number; - - /** -- * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). -+ * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). - * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), - * or by name (e.g., `name-layer-4` for a PSD layer). - * -- * {@link https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files|Vector and Animated Images - Thumbnail Extraction} -+ * [Vector and Animated Images - Thumbnail Extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files) - */ - page?: number | string; - - /** -- * Pass any transformation not directly supported by the SDK. -+ * Pass any transformation not directly supported by the SDK. - * This transformation string is appended to the URL as provided. - */ - raw?: string; - - - /** -- * Specifies an overlay to be applied on the parent image or video. -+ * Specifies an overlay to be applied on the parent image or video. - * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. - * -- * {@link https://imagekit.io/docs/transformations#overlay-using-layers|Transformations - Overlay Using Layers} -+ * [Transformations - Overlay Using Layers](https://imagekit.io/docs/transformations#overlay-using-layers) - */ - overlay?: Overlay; - } -@@ -408,7 +409,7 @@ export interface BaseOverlay { - * Specifies the overlay's position relative to the parent asset. - * Accepts a JSON object with `x` and `y` (or `focus`) properties. - * -- * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} -+ * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) - */ - position?: OverlayPosition; - -@@ -416,7 +417,7 @@ export interface BaseOverlay { - * Specifies timing information for the overlay (only applicable if the base asset is a video). - * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. - * -- * {@link https://imagekit.io/docs/transformations#position-of-layer|Transformations - Position of Layer} -+ * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) - */ - timing?: OverlayTiming; - } -@@ -495,7 +496,7 @@ export interface TextOverlay extends BaseOverlay { - * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. - */ - -- encoding: "auto" | "plain" | "base64"; -+ encoding?: "auto" | "plain" | "base64"; - - /** - * Control styling of the text overlay. -@@ -521,12 +522,12 @@ export interface ImageOverlay extends BaseOverlay { - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. - */ -- encoding: "auto" | "plain" | "base64"; -+ encoding?: "auto" | "plain" | "base64"; - - /** - * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. - * -- * {@link https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay|Video} -+ * [Image](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) | [Video](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay) - */ - transformation?: Transformation[]; - } -@@ -548,12 +549,12 @@ export interface VideoOverlay extends BaseOverlay { - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. - */ -- encoding: "auto" | "plain" | "base64"; -+ encoding?: "auto" | "plain" | "base64"; - - /** - * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. - * -- * {@link https://imagekit.io/docs/video-transformation|Video Transformations} -+ * [Video Transformations](https://imagekit.io/docs/video-transformation) - */ - transformation?: Transformation[]; - } -@@ -575,12 +576,12 @@ export interface SubtitleOverlay extends BaseOverlay { - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. - */ -- encoding: "auto" | "plain" | "base64"; -+ encoding?: "auto" | "plain" | "base64"; - - /** - * Control styling of the subtitle. - * -- * {@link https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer|Styling subtitles} -+ * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - transformation?: SubtitleOverlayTransformation[]; - } -@@ -596,7 +597,7 @@ export interface SolidColorOverlay extends BaseOverlay { - /** - * Control width and height of the solid color overlay. Supported transformations depend on the base/parent asset. - * -- * {@link https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay|Image} | {@link https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay|Video} -+ * [Image](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) | [Video](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay) - */ - transformation?: SolidColorOverlayTransformation[]; - } -diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts -index 9a45479..1eaed4b 100644 ---- a/src/interfaces/UploadOptions.ts -+++ b/src/interfaces/UploadOptions.ts -@@ -16,47 +16,74 @@ interface AbsObject { - - type PostTransformation = TransformationObject | GifToVideoOrThumbnailObject | AbsObject; - --interface Transformation{ -- pre?: string -- post?: PostTransformation[] -+interface Transformation { -+ /** -+ * Specifies pre-transformations to be applied. Must be a valid string of transformations like "w-300,h-300". -+ * Refer to the docs for more details on transformations. -+ * -+ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#pre-transformation} -+ */ -+ pre?: string; -+ -+ /** -+ * Specifies post-transformations to be applied. This is an array of transformation objects, each with: -+ * - type: One of "transformation", "gif-to-video", "thumbnail", or "abs". -+ * - value: A valid transformation string required if "type" is "transformation" or "abs". Optional if "type" is "gif-to-video" or "thumbnail". -+ * - protocol: Used only when type is "abs". Can be "hls" or "dash". -+ * -+ * Refer to the docs for more details on transformations and usage in post. -+ * -+ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#post-transformation} -+ */ -+ post?: PostTransformation[]; - } -+ - /** -- * Options used when uploading a file -- * -- * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Request} -+ * Options used when uploading a file using the V1 API. -+ * Check out the official documentation: -+ * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file} - */ - 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. -+ * This field accepts three main input formats for the file content: -+ * - "binary": Directly pass the binary data. Typically used when uploading via the browser using a File or Blob. -+ * - "base64": A base64-encoded string of the file content. -+ * - "url": A direct URL from which ImageKit server will download the file and upload. - */ - file: string | Blob | File; -+ - /** -- * HMAC-SHA1 digest of the token+expire using your ImageKit.io private API key as a key. This should be in lowercase. -- * Warning: Signature must be calculated on the server-side. This field is required for authentication when uploading a file from the client-side. -+ * The name with which the file should be uploaded. -+ * - Valid characters: alphanumeric (a-z, A-Z, 0-9, including Unicode letters and numerals) and the special chars ". _ -" -+ * - Any other character (including space) is replaced with "_" -+ * -+ * Example: "company_logo.png" -+ */ -+ fileName: string; -+ -+ /** -+ * The HMAC-SHA1 digest of the concatenation of "token + expire". The signing key is your ImageKit private API key. -+ * Required for client-side authentication. Must be computed on the server side. -+ * Calculate this signature in your secure server and pass it to the client. - */ - signature: string; -+ - /** -- * A unique value generated by the client, which will be used by the ImageKit.io server 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. -- * 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. -+ * A unique value to identify and prevent replays. Typically a UUID (e.g., version 4). -+ * Each request must carry a fresh token. The server rejects reused tokens, even if the original request failed. - */ - token: string; -+ - /** -- * The time until your signature is valid. It must be a Unix time in less than 1 hour into the future. It should be in seconds. -+ * A Unix timestamp in seconds, less than 1 hour in the future. - */ - expire: number; -+ - /** -- * The name with which the file has to be uploaded. -- * The file name 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 _ -+ * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). - */ -- fileName: string; -+ publicKey: string; -+ - /** - * Whether to use a unique filename for this file or not. - * - Accepts true or false. -@@ -65,85 +92,105 @@ export interface UploadOptions { - * 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. -+ * Optionally set tags on the uploaded file. -+ * If passing an array, the SDK automatically joins them into a comma-separated string when sending to the server. -+ * Example: ["t-shirt", "round-neck", "men"] => "t-shirt,round-neck,men" - */ - 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 (including unicode letters, marks, and numerals in other languages) -- * - Special Characters: / _ and - -- * - Using multiple / creates a nested folder. -- * Default value - / -+ * The folder path where the file will be stored, e.g., "/images/folder/". -+ * - If the path doesn't exist, it is created on-the-fly. -+ * - Nested folders are supported by using multiple "/". -+ * - Default: "/" - */ - 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 -+ * Whether to mark the file as private (only relevant for image uploads). -+ * A private file requires signed URLs or named transformations for access. -+ * Default: 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. -+ * A string in "x,y,width,height" format that defines the region of interest in an image (top-left coords and area size). -+ * Example: "10,10,100,100". - */ - 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. -+ * A comma-separated or array-based set of fields to return in the upload response. -+ * Example: "tags,customCoordinates,isPrivateFile,metadata" - */ - responseFields?: string | string[]; -- /* -- * Object with array of extensions to be processed on the image. -+ -+ /** -+ * An array of extension objects to apply to the image, e.g. background removal, auto-tagging, etc. -+ * The SDK will JSON-stringify this array automatically before sending. - */ - extensions?: object[]; -- /* -- * Final status of pending extensions will be sent to this URL. -+ -+ /** -+ * A webhook URL to receive the final status of any pending extensions once they've completed processing. -+ */ -+ webhookUrl?: string; -+ -+ /** -+ * Defaults to true. If false, and "useUniqueFileName" is also false, the API immediately fails if a file with the same name/folder already exists. -+ */ -+ overwriteFile?: boolean; -+ -+ /** -+ * Defaults to true. If true, and an existing file is found at the same location, its AITags are removed. Set to false to keep existing AITags. - */ -- webhookUrl?: string -- /* -- * Default is true. If overwriteFile is set to false and useUniqueFileName is also false, and a file already exists at the exact location, upload API will return an error immediately. -+ overwriteAITags?: boolean; -+ -+ /** -+ * Defaults to true. If no tags are specified in the request, existing tags are removed from overwritten files. Setting to false has no effect if the request includes tags. - */ -- overwriteFile?: boolean -- /* -- * Default is true. 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. -+ overwriteTags?: boolean; -+ -+ /** -+ * Defaults to true. If no customMetadata is specified in the request, existing customMetadata is removed from overwritten files. Setting to false has no effect if the request specifies customMetadata. - */ -- overwriteAITags?: boolean -- /* -- * Default is true. If the request does not have tags , overwriteTags is set to true and a file already exists at the exact location, existing tags will be removed. -- * In case the request body has tags, setting overwriteTags to false has no effect and request's tags are set on the asset. -+ overwriteCustomMetadata?: boolean; -+ -+ /** -+ * A stringified JSON or an object containing custom metadata fields to store with the file. -+ * Custom metadata fields must be pre-defined in your ImageKit configuration. - */ -- overwriteTags?: boolean -- /* -- * Default is true. If the request does not have customMetadata , overwriteCustomMetadata is set to true and a file already exists at the exact location, exiting customMetadata will be removed. -- * In case the request body has customMetadata, setting overwriteCustomMetadata to false has no effect and request's customMetadata is set on the asset. -+ customMetadata?: string | Record>; -+ -+ /** -+ * Defines pre and post transformations to be applied to the file during upload. The SDK enforces certain validation rules for pre/post transformations. -+ * For details, see: -+ * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload} - */ -- overwriteCustomMetadata?: boolean -- /* -- * Stringified JSON key-value data to be associated with the asset. Checkout overwriteCustomMetadata parameter to understand default behaviour. -- * Before setting any custom metadata on an asset you have to create the field using custom metadata fields API. -+ transformation?: Transformation; -+ -+ /** -+ * An optional XMLHttpRequest instance for the upload. The SDK merges it with its own logic to handle progress events, etc. -+ * You can listen to `progress` or other events on this object for custom logic. - */ -- customMetadata?: string | Record> -+ xhr?: XMLHttpRequest; - -- transformation?: Transformation -+ /** -+ * A string specifying the checks to be performed server-side before uploading to the media library, e.g. size or mime type checks. -+ * For format details, see: {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#upload-api-checks} -+ */ -+ checks?: string; - - /** -- * Optional XMLHttpRequest object that you can send for upload API request. You can listen to `progress` and other events on this object for any custom logic. -+ * Optional callback function that will be called with the progress event when the file is being uploaded. - */ -- xhr?: XMLHttpRequest -+ onProgress?: (event: ProgressEvent) => void; - - /** -- * Optional `checks` parameters can be used to run server-side checks before files are uploaded to the Media Library. -+ * An AbortSignal instance that can be used to cancel the request if needed. -+ * When aborted, the request fails with an ImageKitAbortError. - */ -- checks?: string -+ abortSignal?: AbortSignal; - } -diff --git a/src/interfaces/UploadResponse.ts b/src/interfaces/UploadResponse.ts -index b38cf27..dbae9c0 100644 ---- a/src/interfaces/UploadResponse.ts -+++ b/src/interfaces/UploadResponse.ts -@@ -6,7 +6,7 @@ - * - * {@link https://imagekit.io/docs/api-reference/digital-asset-management-dam/list-and-search-assets} - */ --export type FileType = "all" | "image" | "non-image"; -+type FileType = "all" | "image" | "non-image"; - - /** - * Metadata object structure -@@ -23,7 +23,7 @@ export type FileType = "all" | "image" | "non-image"; - * - * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on the image's contents. It is different from cryptographic hash functions like MD5 and SHA1. pHash provides similar hash value after minor distortions, like small rotations, blurring, and compression in the image. - */ --export interface Metadata { -+interface Metadata { - height: number; - width: number; - size: number; -@@ -94,8 +94,14 @@ export interface Metadata { - }; - } - -+export interface ResponseMetadata { -+ statusCode: number; -+ requestId: string; -+ headers: Record; -+} -+ - /** -- * Response from uploading a file -+ * Response from server when file is uploaded successfully. - * - * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Responses} - */ -@@ -103,39 +109,39 @@ 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; -+ fileId?: string; - /** - * The name of the uploaded file. - */ -- name: string; -+ name?: string; - /** - * The URL of the file. - */ -- url: string; -+ url?: string; - /** - * In case of an image, a small thumbnail URL. - */ -- thumbnailUrl: string; -+ thumbnailUrl?: string; - /** - * Height of the uploaded image file. Only applicable when file type is image. - */ -- height: number; -+ height?: number; - /** - * Width of the uploaded image file. Only applicable when file type is image. - */ -- width: number; -+ width?: number; - /** - * Size of the uploaded file in bytes. - */ -- size: number; -+ size?: number; - /** - * Type of file. It can either be image or non-image. - */ -- fileType: FileType; -+ fileType?: FileType; - /** - * The path of the file uploaded. It includes any folder that you specified while uploading. - */ -- filePath: string; -+ filePath?: string; - /** - * Array of tags associated with the image. - */ -@@ -143,11 +149,11 @@ export interface UploadResponse { - /** - * Is the file marked as private. It can be either true or false. - */ -- isPrivateFile: boolean; -+ isPrivateFile?: boolean; - /** - * Value of custom coordinates associated with the image in format x,y,width,height. - */ -- customCoordinates: string | null; -+ customCoordinates?: string | null; - /** - * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. - */ -@@ -156,8 +162,21 @@ export interface UploadResponse { - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. - */ - AITags?: object[]; -+ - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } -+ -+ /** -+ * Message indicating that the file upload is accepted. This field is only present when the upload is accepted but not yet processed. -+ * This can happen when the file is being processed for pre-transformation for video. -+ * The upload will be completed once the pre-transformation is done. -+ */ -+ message?: string -+ -+ /** -+ * Response metadata for debugging purposes. -+ */ -+ readonly $ResponseMetadata: ResponseMetadata; - } -diff --git a/src/interfaces/UrlOptions.ts b/src/interfaces/UrlOptions.ts -deleted file mode 100644 -index 5d1d38c..0000000 ---- a/src/interfaces/UrlOptions.ts -+++ /dev/null -@@ -1,55 +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. -- * -- * {@link https://imagekit.io/docs/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 | number }; -- /** -- * 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; --} -- --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 -- * -- * {@link https://github.com/imagekit-developer/imagekit-javascript#url-generation} -- */ --export type UrlOptions = UrlOptionsSrc | UrlOptionsPath; -diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts -index 50afe5c..5aacf10 100644 ---- a/src/interfaces/index.ts -+++ b/src/interfaces/index.ts -@@ -1,7 +1,7 @@ --import { ImageKitOptions } from "./ImageKitOptions"; --import { TransformationPosition } from "./Transformation"; --import { UploadOptions } from "./UploadOptions"; --import { UploadResponse, FileType } from "./UploadResponse"; --import { UrlOptions } from "./UrlOptions"; -+// src/interfaces/index.ts -+// Re-export all interfaces so that TypeDoc includes referenced types in the documentation - --export type { ImageKitOptions, TransformationPosition, UploadOptions, UploadResponse, FileType, UrlOptions }; -+export * from './UploadResponse'; -+export * from './UploadOptions'; -+export * from './Transformation'; -+export * from './SrcOptions'; -diff --git a/src/upload.ts b/src/upload.ts -new file mode 100644 -index 0000000..e5a8fea ---- /dev/null -+++ b/src/upload.ts -@@ -0,0 +1,272 @@ -+import errorMessages from "./constants/errorMessages"; -+import type { ResponseMetadata, UploadOptions, UploadResponse } from "./interfaces"; -+ -+/** -+ * Represents an error when a request to ImageKit is invalid. -+ */ -+export class ImageKitInvalidRequestError extends Error { -+ /** -+ * Optional metadata about the response. It is only available if server returns a response. -+ */ -+ readonly $ResponseMetadata?: ResponseMetadata; -+ constructor(message: string, responseMetadata?: ResponseMetadata) { -+ super(message); -+ this.name = "ImageKitInvalidRequestError"; -+ this.$ResponseMetadata = responseMetadata; -+ } -+} -+ -+/** -+ * Represents an error when an upload operation is aborted. -+ */ -+export class ImageKitAbortError extends Error { -+ /** -+ * The reason why the operation was aborted, which can be any JavaScript value. If not specified, the reason is set to "AbortError" DOMException. -+ */ -+ reason?: unknown; -+ constructor(message: string, reason?: unknown) { -+ super(message); -+ this.name = "ImageKitAbortError"; -+ this.reason = reason; -+ } -+} -+ -+/** -+ * Represents a network error during an upload operation to ImageKit. -+ */ -+export class ImageKitUploadNetworkError extends Error { -+ constructor(message: string) { -+ super(message); -+ this.name = "ImageKitUploadNetworkError"; -+ } -+} -+ -+/** -+ * Represents a server error from ImageKit during an upload operation. -+ */ -+export class ImageKitServerError extends Error { -+ /** -+ * Optional metadata about the response. It is only available if server returns a response. -+ */ -+ readonly $ResponseMetadata?: ResponseMetadata; -+ constructor(message: string, responseMetadata?: ResponseMetadata) { -+ super(message); -+ this.name = "ImageKitServerError"; -+ this.$ResponseMetadata = responseMetadata; -+ } -+} -+ -+/** -+ * Uploads a file to ImageKit with the given upload options. This function uses V1 API, check the [API docs](https://imagekit.io/docs/api-reference/upload-file/upload-file) for more details. -+ * -+ * @throws {ImageKitInvalidRequestError} If the request is invalid. -+ * @throws {ImageKitAbortError} If the request is aborted. -+ * @throws {ImageKitUploadNetworkError} If there is a network error. -+ * @throws {ImageKitServerError} If there is a server error. -+ * -+ * @param {UploadOptions} uploadOptions - The options for uploading the file. -+ * @returns {Promise} A Promise resolving to a successful UploadResponse. -+ */ -+export const upload = (uploadOptions: UploadOptions): Promise => { -+ if(!uploadOptions) { -+ return Promise.reject(new ImageKitInvalidRequestError("Invalid options provided for upload")); -+ } -+ return new Promise((resolve, reject) => { -+ const { xhr: userProvidedXHR } = uploadOptions || {}; -+ delete uploadOptions.xhr; -+ const xhr = userProvidedXHR || new XMLHttpRequest(); -+ -+ if (!uploadOptions.file) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILE_PARAMETER.message)); -+ } -+ -+ if (!uploadOptions.fileName) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_UPLOAD_FILENAME_PARAMETER.message)); -+ } -+ -+ if (!uploadOptions.publicKey || uploadOptions.publicKey.length === 0) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_PUBLIC_KEY.message)); -+ } -+ -+ if (!uploadOptions.token) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_TOKEN.message)); -+ } -+ -+ if (!uploadOptions.signature) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_SIGNATURE.message)); -+ } -+ -+ if (!uploadOptions.expire) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.MISSING_EXPIRE.message)); -+ } -+ -+ if (uploadOptions.transformation) { -+ if (!(Object.keys(uploadOptions.transformation).includes("pre") || Object.keys(uploadOptions.transformation).includes("post"))) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_TRANSFORMATION.message)); -+ } -+ if (Object.keys(uploadOptions.transformation).includes("pre") && !uploadOptions.transformation.pre) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_PRE_TRANSFORMATION.message)); -+ } -+ 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)) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); -+ } else if (transformation.type === "transformation" && !transformation.value) { -+ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); -+ } -+ } -+ } else { -+ return reject(new ImageKitInvalidRequestError(errorMessages.INVALID_POST_TRANSFORMATION.message)); -+ } -+ } -+ } -+ -+ var formData = new FormData(); -+ let key: keyof typeof uploadOptions; -+ for (key in uploadOptions) { -+ if (key) { -+ if (key === "file" && typeof uploadOptions.file != "string") { -+ formData.set('file', uploadOptions.file, String(uploadOptions.fileName)); -+ } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { -+ formData.set('tags', uploadOptions.tags.join(",")); -+ } else if (key === 'signature') { -+ formData.set("signature", uploadOptions.signature); -+ } else if (key === 'expire') { -+ formData.set("expire", String(uploadOptions.expire)); -+ } else if (key === 'token') { -+ formData.set("token", uploadOptions.token); -+ } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { -+ formData.set('responseFields', uploadOptions.responseFields.join(",")); -+ } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { -+ formData.set('extensions', JSON.stringify(uploadOptions.extensions)); -+ } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && -+ !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { -+ formData.set('customMetadata', JSON.stringify(uploadOptions.customMetadata)); -+ } else if (key === "transformation" && typeof uploadOptions.transformation === "object" && -+ uploadOptions.transformation !== null) { -+ formData.set(key, JSON.stringify(uploadOptions.transformation)); -+ } else if (key === 'checks' && uploadOptions.checks) { -+ formData.set("checks", uploadOptions.checks); -+ } else if (uploadOptions[key] !== undefined) { -+ if (["onProgress", "abortSignal"].includes(key)) continue; -+ formData.set(key, String(uploadOptions[key])); -+ } -+ } -+ } -+ -+ if (uploadOptions.onProgress) { -+ xhr.upload.onprogress = function (event: ProgressEvent) { -+ if (uploadOptions.onProgress) uploadOptions.onProgress(event) -+ }; -+ } -+ -+ function onAbortHandler() { -+ xhr.abort(); -+ return reject(new ImageKitAbortError( -+ "Upload aborted", -+ uploadOptions.abortSignal?.reason -+ )); -+ } -+ -+ if (uploadOptions.abortSignal) { -+ if (uploadOptions.abortSignal.aborted) { -+ // If the signal is already aborted, return immediately with the reason -+ -+ return reject(new ImageKitAbortError( -+ "Upload aborted", -+ uploadOptions.abortSignal?.reason -+ )); -+ } -+ -+ // If the signal is not already aborted, add an event listener to abort the request when the signal is aborted -+ uploadOptions.abortSignal.addEventListener("abort", onAbortHandler); -+ -+ // On XHR completion (success, fail, or abort), remove just this abort handler -+ xhr.addEventListener("loadend", () => { -+ if (uploadOptions.abortSignal) { -+ uploadOptions.abortSignal.removeEventListener("abort", onAbortHandler); -+ } -+ }); -+ } -+ -+ xhr.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); -+ xhr.onerror = function (e) { -+ return reject(new ImageKitUploadNetworkError(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR.message)); -+ } -+ xhr.onload = function () { -+ if (xhr.status >= 200 && xhr.status < 300) { -+ try { -+ var body = JSON.parse(xhr.responseText); -+ var uploadResponse = addResponseHeadersAndBody(body, xhr); -+ return resolve(uploadResponse); -+ } catch (ex: any) { -+ return reject(ex); -+ } -+ } else if (xhr.status >= 400 && xhr.status < 500) { -+ // Send ImageKitInvalidRequestError -+ try { -+ var body = JSON.parse(xhr.responseText); -+ return reject(new ImageKitInvalidRequestError( -+ body.message ?? "Invalid request. Please check the parameters.", -+ getResponseMetadata(xhr) -+ )); -+ } catch (ex: any) { -+ return reject(ex); -+ } -+ } else { -+ // Send ImageKitServerError -+ try { -+ var body = JSON.parse(xhr.responseText); -+ return reject(new ImageKitServerError( -+ body.message ?? "Server error occurred while uploading the file. This is rare and usually temporary.", -+ getResponseMetadata(xhr) -+ )); -+ } catch (ex: any) { -+ return reject(new ImageKitServerError( -+ "Server error occurred while uploading the file. This is rare and usually temporary.", -+ getResponseMetadata(xhr) -+ )); -+ } -+ } -+ }; -+ xhr.send(formData); -+ }); -+}; -+ -+ -+const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest) => { -+ let response = { ...body }; -+ const responseMetadata = getResponseMetadata(xhr); -+ Object.defineProperty(response, "$ResponseMetadata", { -+ value: responseMetadata, -+ enumerable: false, -+ writable: false -+ }); -+ return response; -+} -+ -+const getResponseMetadata = (xhr: XMLHttpRequest): ResponseMetadata => { -+ const headers = getResponseHeaderMap(xhr); -+ const responseMetadata = { -+ statusCode: xhr.status, -+ headers: headers, -+ requestId: headers["x-request-id"] -+ } -+ return responseMetadata; -+} -+ -+function getResponseHeaderMap(xhr: XMLHttpRequest): Record { -+ const headers: Record = {}; -+ const responseHeaders = xhr.getAllResponseHeaders(); -+ if (Object.keys(responseHeaders).length) { -+ responseHeaders -+ .trim() -+ .split(/[\r\n]+/) -+ .map(value => value.split(/: /)) -+ .forEach(keyValue => { -+ headers[keyValue[0].trim().toLowerCase()] = keyValue[1].trim(); -+ }); -+ } -+ return headers; -+} -diff --git a/src/upload/index.ts b/src/upload/index.ts -deleted file mode 100644 -index 3d4915a..0000000 ---- a/src/upload/index.ts -+++ /dev/null -@@ -1,104 +0,0 @@ --import errorMessages from "../constants/errorMessages"; --import respond from "../utils/respond"; --import { request } from "../utils/request"; --import { ImageKitOptions, UploadOptions, UploadResponse } from "../interfaces"; -- --export const upload = ( -- xhr: XMLHttpRequest, -- uploadOptions: UploadOptions, -- options: ImageKitOptions, -- callback?: (err: Error | null, response: UploadResponse | null) => void, --) => { -- 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 (!options.publicKey) { -- respond(true, errorMessages.MISSING_PUBLIC_KEY, callback); -- return; -- } -- -- if(!uploadOptions.token) { -- respond(true, errorMessages.MISSING_TOKEN, callback) -- return -- } -- -- if(!uploadOptions.signature) { -- respond(true, errorMessages.MISSING_SIGNATURE, callback) -- return -- } -- -- if(!uploadOptions.expire) { -- respond(true, errorMessages.MISSING_EXPIRE, 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 = new FormData(); -- let key: keyof typeof uploadOptions; -- for (key in uploadOptions) { -- if (key) { -- if (key === "file" && typeof uploadOptions.file != "string") { -- formData.append('file', uploadOptions.file, String(uploadOptions.fileName)); -- } else if (key === "tags" && Array.isArray(uploadOptions.tags)) { -- formData.append('tags', uploadOptions.tags.join(",")); -- } else if (key === 'signature') { -- formData.append("signature", uploadOptions.signature); -- } else if (key === 'expire') { -- formData.append("expire", String(uploadOptions.expire)); -- } else if (key === 'token') { -- formData.append("token", uploadOptions.token); -- } else if (key === "responseFields" && Array.isArray(uploadOptions.responseFields)) { -- formData.append('responseFields', uploadOptions.responseFields.join(",")); -- } else if (key === "extensions" && Array.isArray(uploadOptions.extensions)) { -- formData.append('extensions', JSON.stringify(uploadOptions.extensions)); -- } else if (key === "customMetadata" && typeof uploadOptions.customMetadata === "object" && -- !Array.isArray(uploadOptions.customMetadata) && uploadOptions.customMetadata !== null) { -- formData.append('customMetadata', JSON.stringify(uploadOptions.customMetadata)); -- } else if(key === "transformation" && typeof uploadOptions.transformation === "object" && -- uploadOptions.transformation !== null) { -- formData.append(key, JSON.stringify(uploadOptions.transformation)); -- } else if (key === 'checks' && uploadOptions.checks) { -- formData.append("checks", uploadOptions.checks); -- } else if(uploadOptions[key] !== undefined) { -- formData.append(key, String(uploadOptions[key])); -- } -- } -- } -- -- formData.append("publicKey", options.publicKey); -- -- request(xhr, formData, callback); --}; -diff --git a/src/url/builder.ts b/src/url.ts -similarity index 83% -rename from src/url/builder.ts -rename to src/url.ts -index cc49a1b..836bb8e 100644 ---- a/src/url/builder.ts -+++ b/src/url.ts -@@ -1,6 +1,6 @@ --import { ImageKitOptions, UrlOptions } from "../interfaces"; --import { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "../interfaces/Transformation"; --import transformationUtils, { safeBtoa } from "../utils/transformation"; -+import type { SrcOptions } from "./interfaces"; -+import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; -+import transformationUtils, { safeBtoa } from "./utils/transformation"; - const TRANSFORMATION_PARAMETER = "tr"; - const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') - const SIMPLE_OVERLAY_TEXT_REGEX = new RegExp('^[a-zA-Z0-9-._ ]*$') // These characters are selected by testing actual URLs on both path and query parameters. If and when backend starts supporting wide range of characters, this regex should be updated to improve URL readability. -@@ -25,17 +25,29 @@ function pathJoin(parts: string[], sep?: string) { - return parts.join(separator).replace(replace, separator); - } - --export const buildURL = (opts: UrlOptions & ImageKitOptions) => { -- if (!opts.path && !opts.src) { -+/** -+ * Builds a source URL with the given options. -+ * -+ * @param {SrcOptions} opts - The options for building the source URL. -+ * @returns {string} The constructed source URL. -+ */ -+export const buildSrc = (opts: SrcOptions): string => { -+ opts.urlEndpoint = opts.urlEndpoint || ""; -+ opts.src = opts.src || ""; -+ opts.transformationPosition = opts.transformationPosition || "query"; -+ -+ if (!opts.src) { - return ""; - } - -+ const isAbsoluteURL = opts.src.startsWith("http://") || opts.src.startsWith("https://"); -+ - var urlObj, isSrcParameterUsedForURL, urlEndpointPattern; - - try { -- if (opts.path) { -+ if (!isAbsoluteURL) { - urlEndpointPattern = new URL(opts.urlEndpoint).pathname; -- urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.path])); -+ urlObj = new URL(pathJoin([opts.urlEndpoint.replace(urlEndpointPattern, ""), opts.src])); - } else { - urlObj = new URL(opts.src!); - isSrcParameterUsedForURL = true; -@@ -49,7 +61,7 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { - urlObj.searchParams.append(i, String(opts.queryParameters[i])); - } - -- var transformationString = constructTransformationString(opts.transformation); -+ var transformationString = buildTransformationString(opts.transformation); - - if (transformationString && transformationString.length) { - if (!transformationUtils.addAsQueryParameter(opts) && !isSrcParameterUsedForURL) { -@@ -57,7 +69,7 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { - TRANSFORMATION_PARAMETER + transformationUtils.getChainTransformDelimiter() + transformationString, - urlObj.pathname, - ]); -- } -+ } - } - - if (urlEndpointPattern) { -@@ -67,8 +79,8 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { - } - - if (transformationString && transformationString.length) { -- if(transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { -- if(urlObj.searchParams.toString() !== "") { // In 12 node.js .size was not there. So, we need to check if it is an object or not. -+ if (transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL) { -+ if (urlObj.searchParams.toString() !== "") { // In 12 node.js .size was not there. So, we need to check if it is an object or not. - return `${urlObj.href}&${TRANSFORMATION_PARAMETER}=${transformationString}`; - } - else { -@@ -83,10 +95,10 @@ export const buildURL = (opts: UrlOptions & ImageKitOptions) => { - function processInputPath(str: string, enccoding: string): string { - // Remove leading and trailing slashes - str = removeTrailingSlash(removeLeadingSlash(str)); -- if(enccoding === "plain") { -+ if (enccoding === "plain") { - return `i-${str.replace(/\//g, "@@")}`; - } -- if(enccoding === "base64") { -+ if (enccoding === "base64") { - return `ie-${encodeURIComponent(safeBtoa(str))}`; - } - if (SIMPLE_OVERLAY_PATH_REGEX.test(str)) { -@@ -111,13 +123,11 @@ function processText(str: string, enccoding: TextOverlay["encoding"]): string { - - function processOverlay(overlay: Transformation["overlay"]): string | undefined { - const entries = []; -- if (!overlay) { -- return; -- } -- const { type, position = {}, timing = {}, transformation = [] } = overlay; -+ -+ const { type, position = {}, timing = {}, transformation = [] } = overlay || {}; - - if (!type) { -- throw new Error("Overlay type is required"); -+ return; - } - - switch (type) { -@@ -205,7 +215,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined - entries.push(`ldu-${duration}`); - } - -- const transformationString = constructTransformationString(transformation); -+ const transformationString = buildTransformationString(transformation); - - if (transformationString && transformationString.trim() !== "") entries.push(transformationString); - -@@ -214,7 +224,13 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined - return entries.join(transformationUtils.getTransformDelimiter()); - } - --function constructTransformationString(transformation: Transformation[] | undefined) { -+/** -+ * Builds a transformation string from the given transformations. -+ * -+ * @param {Transformation[] | undefined} transformation - The transformations to apply. -+ * @returns {string} The constructed transformation string. -+ */ -+export const buildTransformationString = function (transformation: Transformation[] | undefined): string { - if (!Array.isArray(transformation)) { - return ""; - } -diff --git a/src/url/index.ts b/src/url/index.ts -deleted file mode 100644 -index 8503b76..0000000 ---- a/src/url/index.ts -+++ /dev/null -@@ -1,12 +0,0 @@ --/* -- URL builder --*/ --import { ImageKitOptions, UrlOptions } from "../interfaces"; --import { buildURL } from "./builder"; -- --export const url = (urlOpts: UrlOptions, defaultOptions: ImageKitOptions) => { -- return buildURL({ -- ...defaultOptions, -- ...urlOpts, -- }); --}; -diff --git a/src/utils/request.ts b/src/utils/request.ts -deleted file mode 100644 -index fd7688d..0000000 ---- a/src/utils/request.ts -+++ /dev/null -@@ -1,83 +0,0 @@ --import respond from "../utils/respond"; --import errorMessages from "../constants/errorMessages" --import { ImageKitOptions, UploadResponse } from "../interfaces"; --import IKResponse from "../interfaces/IKResponse"; -- --interface SignatureResponse { -- signature: string -- expire: number -- token: string --} -- --function getResponseHeaderMap(xhr: XMLHttpRequest) { -- const headers: Record = {}; -- const responseHeaders = xhr.getAllResponseHeaders(); -- if (Object.keys(responseHeaders).length) { -- responseHeaders -- .trim() -- .split(/[\r\n]+/) -- .map(value => value.split(/: /)) -- .forEach(keyValue => { -- headers[keyValue[0].trim()] = keyValue[1].trim(); -- }); -- } -- return headers; --} -- --const addResponseHeadersAndBody = (body: any, xhr: XMLHttpRequest): IKResponse => { -- let response = { ...body }; -- const responseMetadata = { -- statusCode: xhr.status, -- headers: getResponseHeaderMap(xhr) -- } -- Object.defineProperty(response, "$ResponseMetadata", { -- value: responseMetadata, -- enumerable: false, -- writable: false -- }); -- return response as IKResponse; --} -- --export const request = ( -- uploadFileXHR: XMLHttpRequest, -- formData: FormData, -- callback?: (err: Error | null, response: UploadResponse | null) => void) => { -- -- uploadFile(uploadFileXHR, formData).then((result) => { -- return respond(false, result, callback); -- }, (ex) => { -- return respond(true, ex, callback); -- }); --} -- --export const uploadFile = ( -- uploadFileXHR: XMLHttpRequest, -- formData: FormData --): Promise | Error> => { -- return new Promise((resolve, reject) => { -- uploadFileXHR.open('POST', 'https://upload.imagekit.io/api/v1/files/upload'); -- uploadFileXHR.onerror = function (e) { -- return reject(errorMessages.UPLOAD_ENDPOINT_NETWORK_ERROR); -- } -- uploadFileXHR.onload = function () { -- if (uploadFileXHR.status === 200) { -- try { -- var body = JSON.parse(uploadFileXHR.responseText); -- var uploadResponse = addResponseHeadersAndBody(body, uploadFileXHR); -- return resolve(uploadResponse); -- } catch (ex: any) { -- return reject(ex); -- } -- } else { -- try { -- var body = JSON.parse(uploadFileXHR.responseText); -- var uploadError = addResponseHeadersAndBody(body, uploadFileXHR); -- return reject(uploadError) -- } catch (ex: any) { -- return reject(ex); -- } -- } -- }; -- uploadFileXHR.send(formData); -- }); --} -diff --git a/src/utils/respond.ts b/src/utils/respond.ts -deleted file mode 100644 -index 06d02f6..0000000 ---- a/src/utils/respond.ts -+++ /dev/null -@@ -1,9 +0,0 @@ --export default function(isError: boolean, response: any, callback?: (err: Error | null, response: any) => void) { -- if(typeof callback == "function") { -- if(isError) { -- callback(response, null); -- } else { -- callback(null, response); -- } -- } --}; -\ No newline at end of file -diff --git a/src/utils/transformation.ts b/src/utils/transformation.ts -index 5034d0f..324fef0 100644 ---- a/src/utils/transformation.ts -+++ b/src/utils/transformation.ts -@@ -1,25 +1,16 @@ - import supportedTransforms from "../constants/supportedTransforms"; --import { ImageKitOptions, TransformationPosition } from "../interfaces"; -+import { TransformationPosition, SrcOptions } from "../interfaces"; - - const QUERY_TRANSFORMATION_POSITION: TransformationPosition = "query"; - const PATH_TRANSFORMATION_POSITION: TransformationPosition = "path"; --const DEFAULT_TRANSFORMATION_POSITION: TransformationPosition = QUERY_TRANSFORMATION_POSITION; --const VALID_TRANSFORMATION_POSITIONS = [PATH_TRANSFORMATION_POSITION, QUERY_TRANSFORMATION_POSITION]; - const CHAIN_TRANSFORM_DELIMITER: string = ":"; - const TRANSFORM_DELIMITER: string = ","; - const TRANSFORM_KEY_VALUE_DELIMITER: string = "-"; - - export default { -- getDefault: (): TransformationPosition => { -- return DEFAULT_TRANSFORMATION_POSITION; -- }, -- addAsQueryParameter: (options: ImageKitOptions) => { -+ addAsQueryParameter: (options: SrcOptions) => { - return options.transformationPosition === QUERY_TRANSFORMATION_POSITION; - }, -- validParameters: (options: ImageKitOptions) => { -- if (typeof options.transformationPosition == "undefined") return false; -- return VALID_TRANSFORMATION_POSITIONS.indexOf(options.transformationPosition) != -1; -- }, - getTransformKey: function (transform: string) { - if (!transform) { return ""; } - -@@ -38,6 +29,7 @@ export default { - - export const safeBtoa = function (str: string): string { - if (typeof window !== "undefined") { -+ /* istanbul ignore next */ - return btoa(str); - } else { - // Node fallback -diff --git a/test/data/index.js b/test/data/index.js -deleted file mode 100644 -index 1ec1645..0000000 ---- a/test/data/index.js -+++ /dev/null -@@ -1,5 +0,0 @@ --module.exports.initializationParams = { -- publicKey: "test_public_key", -- urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -- transformationPosition: "path", --} -\ No newline at end of file -diff --git a/test/initialization.js b/test/initialization.js -deleted file mode 100644 -index b3695ed..0000000 ---- a/test/initialization.js -+++ /dev/null -@@ -1,70 +0,0 @@ --const chai = require("chai"); --const expect = chai.expect; --const initializationParams = require("./data").initializationParams --import ImageKit from "../src/index"; -- -- --describe("Initialization checks", function () { -- var imagekit = new ImageKit(initializationParams); -- -- it('should throw error: options - empty object', function () { -- try { -- new ImageKit({}); -- } catch(err) { -- expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); -- } -- }); -- -- it('should throw error: options - undefined', function () { -- try { -- new ImageKit(); -- } catch(err) { -- expect(err.message).to.be.equal('Missing urlEndpoint during SDK initialization'); -- } -- }); -- -- it('Pass private Key', function () { -- try { -- new ImageKit({ -- urlEndpoint: initializationParams.urlEndpoint, -- privateKey: "should_not_pass" -- }); -- } catch(err) { -- expect(err.message).to.be.equal('privateKey should not be passed on the client side'); -- } -- }); -- -- 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); -- }); -- -- 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'); -- }); -- -- it('only urlEndpoint is required parameter', function () { -- let imagekit = new ImageKit({ -- urlEndpoint: initializationParams.urlEndpoint -- }); -- -- expect(imagekit.options).to.be.an('object'); -- expect(imagekit.options).to.have.property('urlEndpoint').to.be.equal(initializationParams.urlEndpoint); -- expect(imagekit.url).to.exist.and.to.be.a('function'); -- expect(imagekit.upload).to.exist.and.to.be.a('function'); -- -- }); -- -- it('should throw error: invalid transformationPosition', function () { -- try { -- new ImageKit({...initializationParams, transformationPosition: "test"}); -- } catch(err) { -- expect(err.message).to.be.equal('Invalid transformationPosition parameter'); -- } -- }); --}); -\ No newline at end of file -diff --git a/test/setup.js b/test/setup.js -new file mode 100644 -index 0000000..5decfdd ---- /dev/null -+++ b/test/setup.js -@@ -0,0 +1,12 @@ -+// test-setup.js (loaded before your tests) -+global.FormData = require("formdata-node"); -+global.Blob = require("web-file-polyfill").Blob -+global.File = require("web-file-polyfill").File -+global.ProgressEvent = class FakeProgressEvent { -+ constructor(type, init = {}) { -+ this.type = type; -+ this.lengthComputable = init.lengthComputable || false; -+ this.loaded = init.loaded || 0; -+ this.total = init.total || 0; -+ } -+}; -diff --git a/test/upload.js b/test/upload.js -index 5f27bd4..a5c7004 100644 ---- a/test/upload.js -+++ b/test/upload.js -@@ -1,13 +1,15 @@ - const chai = require("chai"); - const sinon = require("sinon"); --global.FormData = require("formdata-node"); --global.Blob = require("web-file-polyfill").Blob --global.File = require("web-file-polyfill").File - const expect = chai.expect; --const initializationParams = require("./data").initializationParams --import ImageKit from "../src/index"; -+import 'regenerator-runtime/runtime'; -+import { -+ ImageKitAbortError, -+ ImageKitInvalidRequestError, -+ ImageKitServerError, -+ ImageKitUploadNetworkError, upload -+} from "../src/index"; -+ - var requests, server; --import 'regenerator-runtime/runtime' - - const uploadSuccessResponseObj = { - "fileId": "598821f949c0a938d57563bd", -@@ -29,8 +31,9 @@ const uploadSuccessResponseObj = { - const securityParameters = { - signature: "test_signature", - expire: 123, -- token: "test_token" --} -+ token: "test_token", -+ publicKey: "test_public_key", -+}; - - function successUploadResponse() { - server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", -@@ -59,17 +62,13 @@ function errorUploadResponse(statusCode, obj) { - } - - async function sleep(ms = 0) { -- return new Promise((resolve, reject) => { -- setTimeout(() => { -- resolve(); -- }, ms); -+ return true; -+ return new Promise((resolve) => { -+ setTimeout(resolve, ms); - }); - } - --describe("File upload", function () { -- -- var imagekit = new ImageKit(initializationParams); -- -+describe("File upload", async function () { - beforeEach(() => { - global.XMLHttpRequest = sinon.useFakeXMLHttpRequest(); - requests = []; -@@ -78,130 +77,161 @@ describe("File upload", function () { - }); - - afterEach(() => { -- // Like before we must clean up when tampering with globals. - global.XMLHttpRequest.restore(); - server.restore(); - }); - -- it('Invalid options', function () { -- var callback = sinon.spy(); -- -- imagekit.upload(undefined, callback); -- expect(server.requests.length).to.be.equal(0); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { help: "", message: "Invalid uploadOptions parameter" }, null); -+ it('Invalid options', async function () { -+ try { -+ await upload(); -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid options provided for upload"); -+ } - }); - -- it('Missing fileName', function () { -+ it('Missing fileName', async function () { - const fileOptions = { - ...securityParameters, - file: "https://ik.imagekit.io/remote-url.jpg" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { help: "", message: "Missing fileName parameter for upload" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing fileName parameter for upload"); -+ } - }); - -- it('Missing file', function () { -+ it('Missing file', async function () { - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { help: "", message: "Missing file parameter for upload" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing file parameter for upload"); -+ } - }); -- -- it('Missing token', function () { -+ -+ it('Missing token', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - signature: 'test_signature', -- expire: 123 -+ expire: 123, -+ // Omit token -+ publicKey: 'test_public_key' - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { message: "Missing token for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing token for upload. The SDK expects token, signature and expire for authentication."); -+ } - }); - -- it('Missing signature', function () { -+ it('Missing signature', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - token: 'test_token', -- expire: 123 -+ expire: 123, -+ publicKey: 'test_public_key' -+ // Omit signature - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { message: "Missing signature for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing signature for upload. The SDK expects token, signature and expire for authentication."); -+ } - }); - -- it('Missing expire', function () { -+ it('Missing expire', async function () { - const fileOptions = { - fileName: "test_file_name", - file: "test_file", - token: 'test_token', -- signature: 'test_signature' -+ signature: 'test_signature', -+ publicKey: 'test_public_key' -+ // Omit expire - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, { message: "Missing expire for upload. The SDK expects token, signature and expire for authentication.", help: "" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing expire for upload. The SDK expects token, signature and expire for authentication."); -+ } - }); - -- it('Missing public key', function () { -+ it('Missing public key', async function () { - const fileOptions = { - fileName: "test_file_name", -- file: "test_file" -+ file: "test_file", -+ token: 'test_token', -+ signature: 'test_signature', -+ expire: 123 -+ // Omit publicKey - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback, { -- publicKey: "" -- }); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); -- sinon.assert.calledWith(callback, { message: "Missing public key for upload", help: "" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Missing public key for upload"); -+ } - }); - - it('Upload endpoint network error handling', async function () { - const fileOptions = { -- ...securityParameters, - fileName: "test_file_name", -- file: "test_file" -+ file: "test_file", -+ token: 'test_token', -+ signature: 'test_signature', -+ expire: 123, -+ publicKey: 'test_public_key' - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); -- - // Simulate network error on upload API - server.requests[0].error(); - await sleep(); -- sinon.assert.calledWith(callback, { message: "Request to ImageKit upload endpoint failed due to network error", help: "" }, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitUploadNetworkError).to.be.true; -+ expect(ex.message).to.be.equal("Request to ImageKit upload endpoint failed due to network error"); -+ } - }); - - it('Boolean handling', async function () { -@@ -216,10 +246,7 @@ describe("File upload", function () { - isPrivateFile: true - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -238,8 +265,8 @@ describe("File upload", function () { - expect(arg.get('isPrivateFile')).to.be.equal('true'); - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Tag array handling', async function () { -@@ -252,10 +279,7 @@ describe("File upload", function () { - isPrivateFile: true - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -272,8 +296,8 @@ describe("File upload", function () { - expect(arg.get('isPrivateFile')).to.be.equal('true'); - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Missing useUniqueFileName', async function () { -@@ -285,10 +309,7 @@ describe("File upload", function () { - isPrivateFile: true - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -307,8 +328,8 @@ describe("File upload", function () { - expect(arg.get('customCoordinates')).to.be.equal(undefined); - expect(arg.get('responseFields')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Missing isPrivateFile', async function () { -@@ -319,10 +340,7 @@ describe("File upload", function () { - tags: ["test_tag1", "test_tag2"] - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -341,8 +359,8 @@ describe("File upload", function () { - expect(arg.get('customCoordinates')).to.be.equal(undefined); - expect(arg.get('responseFields')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('With extensions parameter', async function () { -@@ -364,10 +382,7 @@ describe("File upload", function () { - ], - webhookUrl: "https://your-domain/?appId=some-id" - }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -387,10 +402,10 @@ describe("File upload", function () { - expect(arg.get('isPrivateFile')).to.be.equal('true'); - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); -- expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id') -+ expect(arg.get('webhookUrl')).to.be.equal('https://your-domain/?appId=some-id'); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Bare minimum request', async function () { -@@ -401,10 +416,7 @@ describe("File upload", function () { - tags: undefined - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -423,29 +435,26 @@ describe("File upload", function () { - expect(arg.get('customCoordinates')).to.be.equal(undefined); - expect(arg.get('responseFields')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Bare minimum request: Blob', async function () { -- const buffer = Buffer.from("test_buffer") -+ const buffer = Buffer.from("test_buffer"); - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", - file: buffer - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); - await sleep(); - - var arg = server.requests[0].requestBody; -- -+ // It's a blob now, check size - expect(arg.get('file').size).to.be.eq(buffer.length); - expect(arg.get('fileName')).to.be.equal("test_file_name"); - expect(arg.get('token')).to.be.equal("test_token"); -@@ -458,8 +467,8 @@ describe("File upload", function () { - expect(arg.get('customCoordinates')).to.be.equal(undefined); - expect(arg.get('responseFields')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Error during upload', async function () { -@@ -469,20 +478,22 @@ describe("File upload", function () { - file: "test_file" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - var errRes = { - help: "For support kindly contact us at support@imagekit.io .", - message: "Your account cannot be authenticated." -- } -- errorUploadResponse(500, errRes); -+ }; -+ errorUploadResponse(401, errRes); - await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Your account cannot be authenticated."); -+ } - }); - - it('Error during upload non 2xx with bad body', async function () { -@@ -492,10 +503,7 @@ describe("File upload", function () { - file: "test_file" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", -@@ -507,9 +515,14 @@ describe("File upload", function () { - ); - server.respond(); - await sleep(); -- expect(callback.calledOnce).to.be.true; -- var error = callback.args[0][0]; -- expect(error instanceof SyntaxError).to.be.true; -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ // The response body is invalid JSON => SyntaxError -+ expect(ex instanceof ImageKitServerError).to.be.true; -+ expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); -+ } - }); - - it('Error during upload 2xx with bad body', async function () { -@@ -519,10 +532,7 @@ describe("File upload", function () { - file: "test_file" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", -@@ -534,9 +544,12 @@ describe("File upload", function () { - ); - server.respond(); - await sleep(); -- expect(callback.calledOnce).to.be.true; -- var error = callback.args[0][0]; -- expect(error instanceof SyntaxError).to.be.true; -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof SyntaxError).to.be.true; -+ } - }); - - it('Upload via URL', async function () { -@@ -546,10 +559,7 @@ describe("File upload", function () { - file: "https://ik.imagekit.io/remote-url.jpg" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -568,8 +578,8 @@ describe("File upload", function () { - expect(arg.get('customCoordinates')).to.be.equal(undefined); - expect(arg.get('responseFields')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Overriding public key', async function () { -@@ -581,9 +591,8 @@ describe("File upload", function () { - file: "https://ik.imagekit.io/remote-url.jpg" - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback, { -+ const uploadPromise = upload({ -+ ...fileOptions, - publicKey: newPublicKey - }); - -@@ -607,8 +616,8 @@ describe("File upload", function () { - expect(arg.get('extensions')).to.be.equal(undefined); - expect(arg.get('customMetadata')).to.be.equal(undefined); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('With overwrite parameters', async function () { -@@ -622,21 +631,14 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ], - overwriteFile: false, - overwriteAITags: false, - overwriteTags: false, - overwriteCustomMetadata: false - }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -661,8 +663,8 @@ describe("File upload", function () { - expect(arg.get('overwriteTags')).to.be.equal('false'); - expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('With customMetadata', async function () { -@@ -676,11 +678,7 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ], - overwriteFile: false, - overwriteAITags: false, -@@ -691,10 +689,7 @@ describe("File upload", function () { - color: "red" - }, - }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -720,8 +715,8 @@ describe("File upload", function () { - expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); - expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Array type fields', async function () { -@@ -735,11 +730,7 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ], - overwriteFile: false, - overwriteAITags: false, -@@ -750,10 +741,7 @@ describe("File upload", function () { - color: "red" - }, - }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -779,8 +767,8 @@ describe("File upload", function () { - expect(arg.get('overwriteCustomMetadata')).to.be.equal('false'); - expect(arg.get('customMetadata')).to.be.equal(JSON.stringify(fileOptions.customMetadata)); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('check custom XHR object is used', async function () { -@@ -797,16 +785,11 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ], - xhr - }; -- var callback = sinon.spy(); -- imagekit.upload(fileOptions, callback); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - expect(server.requests[0]).to.be.equal(xhr); - expect(server.requests[0].onprogress.toString()).to.be.equal(fun.toString()); -@@ -829,8 +812,8 @@ describe("File upload", function () { - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('Upload using promise - success', async function () { -@@ -844,15 +827,11 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ] - }; - -- var uploadPromise = imagekit.upload(fileOptions); -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - - await sleep(); -@@ -873,15 +852,15 @@ describe("File upload", function () { - expect(arg.get('isPrivateFile')).to.be.equal('true'); - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); -- var response = await uploadPromise; -+ const response = await uploadPromise; - expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - -- it('Upload using promise - error', async function () { -+ it('Server 5xx error with proper json and message', async function () { - var errRes = { - help: "For support kindly contact us at support@imagekit.io .", -- message: "Your account cannot be authenticated." -- } -+ message: "Something went wrong" -+ }; - const fileOptions = { - ...securityParameters, - fileName: "test_file_name", -@@ -892,22 +871,20 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ] - }; - - try { -- var uploadPromise = imagekit.upload(fileOptions); -+ const uploadPromise = upload(fileOptions); - await sleep(); - errorUploadResponse(500, errRes); - await sleep(); -- var response = await uploadPromise; -+ await uploadPromise; -+ throw new Error('Should have thrown error'); - } catch (ex) { -- expect(ex).to.be.deep.equal(errRes); -+ expect(ex instanceof ImageKitServerError).to.be.true; -+ expect(ex.message).to.be.equal("Something went wrong"); - } - }); - -@@ -925,19 +902,14 @@ describe("File upload", function () { - useUniqueFileName: false, - isPrivateFile: true, - extensions: [ -- { -- name: "aws-auto-tagging", -- minConfidence: 80, -- maxTags: 10 -- } -+ { name: "aws-auto-tagging", minConfidence: 80, maxTags: 10 } - ], - xhr - }; -- var uploadPromise = imagekit.upload(fileOptions); -+ const uploadPromise = upload(fileOptions); - - expect(server.requests.length).to.be.equal(1); - -- - await sleep(); - successUploadResponse(); - await sleep(); -@@ -960,13 +932,13 @@ describe("File upload", function () { - expect(arg.get('publicKey')).to.be.equal('test_public_key'); - expect(arg.get('extensions')).to.be.equal(JSON.stringify(fileOptions.extensions)); - -- var response = await uploadPromise; -+ const response = await uploadPromise; - expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it('$ResponseMetadata assertions using promise', async function () { -- var dummyResonseHeaders = { -- "Content-Type": "application/json", -+ var dummyResponseHeaders = { -+ "content-type": "application/json", - "x-request-id": "sdfsdfsdfdsf" - }; - const fileOptions = { -@@ -987,7 +959,7 @@ describe("File upload", function () { - ] - }; - -- var uploadPromise = imagekit.upload(fileOptions) -+ var uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - - await sleep(); -@@ -995,53 +967,17 @@ describe("File upload", function () { - server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", - [ - 200, -- dummyResonseHeaders, -- JSON.stringify(uploadSuccessResponseObj) -- ] -- ); -- server.respond(); -- await sleep(); -- -- var response = await uploadPromise; -- expect(response.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); -- expect(response.$ResponseMetadata.statusCode).to.be.deep.equal(200); -- }); -- -- it('$ResponseMetadata assertions using callback', async function () { -- var dummyResonseHeaders = { -- "Content-Type": "application/json", -- "x-request-id": "sdfsdfsdfdsf" -- }; -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file" -- }; -- var callback = sinon.spy(); -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- -- await sleep(); -- server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", -- [ -- 200, -- dummyResonseHeaders, -+ dummyResponseHeaders, - JSON.stringify(uploadSuccessResponseObj) - ] - ); - server.respond(); - await sleep(); - -- expect(callback.calledOnce).to.be.true; -- -- var callBackArguments = callback.args[0]; -- expect(callBackArguments.length).to.be.eq(2); -- var callbackResult = callBackArguments[1]; -- -- expect(callbackResult).to.be.deep.equal(uploadSuccessResponseObj); -- expect(callbackResult.$ResponseMetadata.headers).to.be.deep.equal(dummyResonseHeaders); -- expect(callbackResult.$ResponseMetadata.statusCode).to.be.deep.equal(200); -+ const response = await uploadPromise; -+ expect(response.$ResponseMetadata.headers).to.deep.equal(dummyResponseHeaders); -+ expect(response.$ResponseMetadata.statusCode).to.equal(200); -+ expect(response.$ResponseMetadata.requestId).to.equal("sdfsdfsdfdsf"); - }); - - it('Undefined fields should not be sent', async function () { -@@ -1063,10 +999,7 @@ describe("File upload", function () { - customMetadata: undefined - }; - -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); -@@ -1088,246 +1021,243 @@ describe("File upload", function () { - expect(arg.get('overwriteTags')).to.be.equal(undefined); - expect(arg.get('overwriteCustomMetadata')).to.be.equal(undefined); - expect(arg.get('customMetadata')).to.be.equal(undefined); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it("With pre and post transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- successUploadResponse(); -- await sleep(); -- -- var arg = server.requests[0].requestBody; -- -- expect(arg.get("file")).to.be.equal("test_file"); -- expect(arg.get("fileName")).to.be.equal("test_file_name"); -- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -- expect(arg.get("useUniqueFileName")).to.be.equal("false"); -- expect(arg.get("publicKey")).to.be.equal("test_public_key"); -- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -- -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { pre: "w-100", post: [{ type: "transformation", value: "w-100" }] }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ successUploadResponse(); -+ await sleep(); -+ -+ var arg = server.requests[0].requestBody; -+ -+ expect(arg.get("file")).to.be.equal("test_file"); -+ expect(arg.get("fileName")).to.be.equal("test_file_name"); -+ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -+ expect(arg.get("useUniqueFileName")).to.be.equal("false"); -+ expect(arg.get("publicKey")).to.be.equal("test_public_key"); -+ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -+ -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it("With pre transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: { pre: "w-100" }, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- successUploadResponse(); -- await sleep(); -- -- var arg = server.requests[0].requestBody; -- -- expect(arg.get("file")).to.be.equal("test_file"); -- expect(arg.get("fileName")).to.be.equal("test_file_name"); -- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -- expect(arg.get("useUniqueFileName")).to.be.equal("false"); -- expect(arg.get("publicKey")).to.be.equal("test_public_key"); -- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -- -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { pre: "w-100" }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ successUploadResponse(); -+ await sleep(); -+ -+ var arg = server.requests[0].requestBody; -+ -+ expect(arg.get("file")).to.be.equal("test_file"); -+ expect(arg.get("fileName")).to.be.equal("test_file_name"); -+ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -+ expect(arg.get("useUniqueFileName")).to.be.equal("false"); -+ expect(arg.get("publicKey")).to.be.equal("test_public_key"); -+ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -+ -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - - it("With post transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: { post: [{ type: "transformation", value: "w-100" }] }, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- successUploadResponse(); -- await sleep(); -- -- var arg = server.requests[0].requestBody; -- -- expect(arg.get("file")).to.be.equal("test_file"); -- expect(arg.get("fileName")).to.be.equal("test_file_name"); -- expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -- expect(arg.get("useUniqueFileName")).to.be.equal("false"); -- expect(arg.get("publicKey")).to.be.equal("test_public_key"); -- expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -- -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { post: [{ type: "transformation", value: "w-100" }] }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ successUploadResponse(); -+ await sleep(); -+ -+ var arg = server.requests[0].requestBody; -+ -+ expect(arg.get("file")).to.be.equal("test_file"); -+ expect(arg.get("fileName")).to.be.equal("test_file_name"); -+ expect(arg.get("responseFields")).to.be.equal("tags, customCoordinates, isPrivateFile, metadata"); -+ expect(arg.get("useUniqueFileName")).to.be.equal("false"); -+ expect(arg.get("publicKey")).to.be.equal("test_public_key"); -+ expect(arg.get("transformation")).to.be.equal(JSON.stringify(fileOptions.transformation)); -+ -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); - }); - -- it("Should return error for an invalid transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: {}, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- var errRes = { -- help: "", -- message: "Invalid transformation parameter. Please include at least pre, post, or both.", -- }; -- errorUploadResponse(500, errRes); -- await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ it("Server 5xx without message", async function () { -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ var errRes = { -+ help: "" -+ }; -+ errorUploadResponse(500, errRes); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitServerError).to.be.true; -+ expect(ex.message).to.be.equal("Server error occurred while uploading the file. This is rare and usually temporary."); -+ } - }); - - it("Should return error for an invalid pre transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: { pre: "" }, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- var errRes = { -- help: "", -- message: "Invalid pre transformation parameter.", -- }; -- errorUploadResponse(500, errRes); -- await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { pre: "" }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ var errRes = { -+ help: "", -+ message: "Invalid pre transformation parameter.", -+ }; -+ errorUploadResponse(500, errRes); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid pre transformation parameter."); -+ } - }); - - it("Should return error for an invalid post transformation of type abs", async function () { -- const fileOptions = { -- ...securityParameters, -- 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); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- var errRes = { -- help: "", -- message: "Invalid post transformation parameter.", -- }; -- errorUploadResponse(500, errRes); -- await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { post: [{ type: "abs", value: "" }] }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ var errRes = { -+ help: "", -+ message: "Invalid post transformation parameter.", -+ }; -+ errorUploadResponse(500, errRes); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid post transformation parameter."); -+ } - }); - - it("Should return error for an invalid post transformation of type transformation", async function () { -- const fileOptions = { -- ...securityParameters, -- 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); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- var errRes = { -- help: "", -- message: "Invalid post transformation parameter.", -- }; -- errorUploadResponse(500, errRes); -- await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { post: [{ type: "transformation", value: "" }] }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ var errRes = { -+ help: "", -+ message: "Invalid post transformation parameter.", -+ }; -+ errorUploadResponse(500, errRes); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid post transformation parameter."); -+ } - }); - - it("Should return error for an invalid post transformation if it's not an array", async function () { -- const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- transformation: { post: {} }, -- }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -- expect(server.requests.length).to.be.equal(1); -- await sleep(); -- var errRes = { -- help: "", -- message: "Invalid post transformation parameter.", -- }; -- errorUploadResponse(500, errRes); -- await sleep(); -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, errRes, null); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ transformation: { post: {} }, -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ await sleep(); -+ var errRes = { -+ help: "", -+ message: "Invalid post transformation parameter.", -+ }; -+ errorUploadResponse(500, errRes); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid post transformation parameter."); -+ } - }); - - it("With checks option", async function () { - const fileOptions = { -- ...securityParameters, -- fileName: "test_file_name", -- file: "test_file", -- responseFields: "tags, customCoordinates, isPrivateFile, metadata", -- useUniqueFileName: false, -- checks: "'request.folder' : '/'", -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ responseFields: "tags, customCoordinates, isPrivateFile, metadata", -+ useUniqueFileName: false, -+ checks: "'request.folder' : '/'", - }; -- var callback = sinon.spy(); -- -- imagekit.upload(fileOptions, callback); -- -+ const uploadPromise = upload(fileOptions); - expect(server.requests.length).to.be.equal(1); - await sleep(); - successUploadResponse(); - await sleep(); -- -+ - var arg = server.requests[0].requestBody; - expect(arg.get("file")).to.be.equal("test_file"); - expect(arg.get("fileName")).to.be.equal("test_file_name"); -@@ -1335,8 +1265,130 @@ describe("File upload", function () { - expect(arg.get("useUniqueFileName")).to.be.equal("false"); - expect(arg.get("publicKey")).to.be.equal("test_public_key"); - expect(arg.get('checks')).to.be.equal("'request.folder' : '/'"); -- -- expect(callback.calledOnce).to.be.true; -- sinon.assert.calledWith(callback, null, uploadSuccessResponseObj); -+ -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); -+ }); -+ -+ it('onProgress callback is triggered during upload', async function () { -+ const progressSpy = sinon.spy(); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ onProgress: progressSpy -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ server.requests[0].uploadProgress({ lengthComputable: true, loaded: 50, total: 100 }); -+ -+ await sleep(); -+ expect(progressSpy.calledOnce).to.be.true; -+ successUploadResponse(); -+ await sleep(); -+ expect(progressSpy.calledTwice).to.be.true; // final progress -+ const response = await uploadPromise; -+ expect(response).to.be.deep.equal(uploadSuccessResponseObj); -+ }); -+ -+ it('Abort signal aborts the upload', async function () { -+ const abortController = new AbortController(); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ abortSignal: abortController.signal -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ abortController.abort(); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitAbortError).to.be.true; -+ expect(ex.reason.name).to.be.equal("AbortError"); -+ } -+ }); -+ -+ it('Abort signal aborts the upload with reason', async function () { -+ const abortController = new AbortController(); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ abortSignal: abortController.signal -+ }; -+ const uploadPromise = upload(fileOptions); -+ expect(server.requests.length).to.be.equal(1); -+ abortController.abort("abort reason"); -+ await sleep(); -+ try { -+ await uploadPromise; -+ throw new Error('Should have thrown error'); -+ } catch (ex) { -+ expect(ex instanceof ImageKitAbortError).to.be.true; -+ expect(ex.reason).to.be.equal("abort reason"); -+ } -+ }); -+ -+ it("Already aborted signal should abort upload immediately", async function () { -+ const abortController = new AbortController(); -+ // Abort the signal before calling upload -+ abortController.abort(); -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ abortSignal: abortController.signal -+ }; -+ try { -+ await upload(fileOptions); -+ throw new Error("Should have thrown error"); -+ } catch (ex) { -+ expect(ex instanceof ImageKitAbortError).to.be.true; -+ expect(ex.reason && ex.reason.name).to.be.equal("AbortError"); -+ } -+ }); -+ -+ it("Error during upload 4xx with invalid JSON response", async function () { -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file" -+ }; -+ const uploadPromise = upload(fileOptions); -+ // errorUploadResponse(400, `{sd`); -+ server.respondWith("POST", "https://upload.imagekit.io/api/v1/files/upload", -+ [ -+ 400, -+ { "Content-Type": "application/json" }, -+ "sdf" -+ ] -+ ); -+ server.respond(); -+ try { -+ await uploadPromise; -+ throw new Error("Should have thrown error"); -+ } catch (ex) { -+ expect(ex).to.be.instanceOf(SyntaxError); -+ } -+ }); -+ -+ it("Should return error for an invalid transformation object in upload", async function () { -+ const fileOptions = { -+ ...securityParameters, -+ fileName: "test_file_name", -+ file: "test_file", -+ transformation: 123 -+ }; -+ try { -+ await upload(fileOptions); -+ throw new Error("Should have thrown error"); -+ } catch (ex) { -+ expect(ex instanceof ImageKitInvalidRequestError).to.be.true; -+ expect(ex.message).to.be.equal("Invalid transformation parameter. Please include at least pre, post, or both."); -+ } - }); - }); -diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js -index cb81ef2..739958c 100644 ---- a/test/url-generation/basic.js -+++ b/test/url-generation/basic.js -@@ -1,36 +1,51 @@ - const chai = require("chai"); --const pkg = require("../../package.json"); --global.FormData = require('formdata-node'); - const expect = chai.expect; --const initializationParams = require("../data").initializationParams --import ImageKit from "../../src/index"; -+import { buildSrc } from "../../src/index"; - - describe("URL generation", function () { -+ it('should return an empty string when src is not provided', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query" -+ }); - -- var imagekit = new ImageKit(initializationParams); -+ expect(url).equal(""); -+ }); - -- it('should return an empty string when neither path nor src is provided', function () { -- const url = imagekit.url({}); -+ it('should return an empty string when src is /', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/" -+ }); - -- expect(url).equal(""); -+ expect(url).equal("https://ik.imagekit.io/test_url_endpoint/"); - }); - -- it('should return an empty string for an invalid src URL', function () { -- const url = imagekit.url({ src: "/" }); -+ it('should return an empty string when src is invalid', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "https://" -+ }); - - expect(url).equal(""); - }); - -- it('should generate a valid URL when a path is provided without transformation', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg" -+ it('should generate a valid URL when src is provided without transformation', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); - }); - - it('should generate a valid URL when a src is provided without transformation', function () { -- const url = imagekit.url({ -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" - }); - -@@ -38,951 +53,1235 @@ describe("URL generation", function () { - }); - - it('should generate a valid URL when undefined transformation parameters are provided with path', function () { -- const url = imagekit.url({ -- path: "/test_path_alt.jpg", -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/test_path_alt.jpg", - transformation: undefined, -- transformationPosition: undefined, -- src: undefined, -+ transformationPosition: "query" - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg`); - }); - - it("By default transformationPosition should be query", function () { -- var imagekitNew = new ImageKit({ -- publicKey: "test_public_key", -+ const url = buildSrc({ - urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -- }); -- const url = imagekitNew.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }, { -- rotation: 90 -- }] -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ }, -+ { -+ rotation: 90 -+ } -+ ] - }); - expect(url).equal("https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90"); - }); - - it('should generate the URL without sdk version', function () { -- const ik = new ImageKit(initializationParams) -- -- const url = ik.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ], -+ transformationPosition: "path" - }); - - 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 a valid path and transformation', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ it('should generate the correct URL with a valid src and transformation', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/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`); -+ // Now transformed URL goes into query since transformationPosition is "query". -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); - }); - - it('should generate the correct URL when the provided path contains multiple leading slashes', function () { -- const url = 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`); -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "///test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ] -+ }); - -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); - }); - - it('should generate the correct URL when the urlEndpoint is overridden', function () { -- const url = imagekit.url({ -+ const url = buildSrc({ -+ // We do not override urlEndpoint here - 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`); -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ] -+ }); - -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=h-300,w-400`); - }); - -- it('should generate the correct URL with transformationPosition as query parameter when path is provided', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -+ it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/test_path.jpg", - transformationPosition: "query", -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400`); - }); - - it('should generate the correct URL with a valid src parameter and transformation', function () { -- const url = imagekit.url({ -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); - }); - - it('should generate the correct URL with transformationPosition as query parameter when src is provided', function () { -- const url = imagekit.url({ -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - transformationPosition: "query", -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ transformation: [ -+ { -+ height: "300", -+ width: "400" -+ } -+ ] - }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400`); - }); - - it('should merge query parameters correctly in the generated URL', function () { -- const url = imagekit.url({ -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", - src: "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", - queryParameters: { t2: "v2", t3: "v3" }, -- transformation: [{ -- "height": "300", -- "width": "400" -- }] -+ 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,w-400`); - }); - -- - it('should generate the correct URL with chained transformations', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }, { -- "rt": "90" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/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`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90`); - }); - -- - it('should generate the correct URL with chained transformations including a new undocumented transformation parameter', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400" -- }, { -- "rndm_trnsf": "abcd" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/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`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rndm_trnsf-abcd`); - }); - - it('should generate the correct URL when overlay image transformation is provided', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400", -- "raw": "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400", -+ raw: "l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end" -+ } -+ ] -+ }); - -- expect(url).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`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-overlay.jpg,w-100,b-10_CDDC39,l-end`); - }); - - it('should generate the correct URL when overlay image transformation contains a slash in the overlay path', function () { -- const url = 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" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400", -+ raw: "l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end" -+ } -+ ] -+ }); - -- expect(url).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`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,l-image,i-/path/to/overlay.jpg,w-100,b-10_CDDC39,l-end`); - }); - - it('should generate the correct URL when border transformation is applied', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "height": "300", -- "width": "400", -- border: "20_FF0000" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: "300", -+ width: "400", -+ border: "20_FF0000" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400,b-20_FF0000/test_path.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,b-20_FF0000`); - }); - - it('should generate the correct URL when transformation has empty key and value', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "": "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ "": "" -+ } -+ ] -+ }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`); - }); - -- /** -- * Provided to provide support to a new transform without sdk update -- */ - it('should generate the correct URL when an undefined transform is provided', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- "undefined-transform": "true" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ "undefined-transform": "true" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:undefined-transform-true/test_path.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=undefined-transform-true`); - }); - - it('should generate the correct URL when transformation key has an empty value', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- defaultImage: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ defaultImage: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-/test_path.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=di-`); - }); - - it('should generate the correct URL when transformation key has \'-\' as its value', function () { -- const url = imagekit.url({ -- path: "/test_path.jpg", -- transformation: [{ -- contrastStretch: "-" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ contrastStretch: "-" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-contrast/test_path.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=e-contrast`); - }); - - it('should skip transformation parameters that are undefined or null', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- quality: undefined, -- contrastStretch: null -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ quality: undefined, -+ contrastStretch: null -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); - }); - - it('should skip transformation parameters that are false', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- contrastStretch: false -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ contrastStretch: false -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg`); - }); - - it('should include only the key when transformation value is an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- shadow: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ shadow: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow`); - }); - - it('should include both key and value when transformation parameter value is provided', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- shadow: "bl-15_st-40_x-10_y-N5" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ shadow: "bl-15_st-40_x-10_y-N5" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,e-shadow-bl-15_st-40_x-10_y-N5`); - }); - - it('should generate the correct URL when trim transformation is set to true as a boolean', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- trim: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ trim: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); - }); - - it('should generate the correct URL when trim transformation is set to true as a string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- defaultImage: "/test_path.jpg", -- trim: "true" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ defaultImage: "/test_path.jpg", -+ trim: "true" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:di-test_path.jpg,t-true/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=di-test_path.jpg,t-true`); - }); - - it('should generate the correct URL for AI background removal when set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackground: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackground: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); - }); - - it('should generate the correct URL for AI background removal when \'true\' is provided as a string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackground: "true" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackground: "true" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-bgremove/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove`); - }); - - it('should not apply AI background removal when value is not true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackground: "false" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackground: "false" -+ } -+ ] -+ }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); - }); - - it('should generate the correct URL for external AI background removal when set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackgroundExternal: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackgroundExternal: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); - }); - - it('should generate the correct URL for external AI background removal when \'true\' is provided as a string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackgroundExternal: "true" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackgroundExternal: "true" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-removedotbg/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg`); - }); - - it('should not apply external AI background removal when value is not true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiRemoveBackgroundExternal: "false" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiRemoveBackgroundExternal: "false" -+ } -+ ] -+ }); - - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg`); - }); - - it('should generate the correct URL when gradient transformation is provided as a string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- gradient: "ld-top_from-green_to-00FF0010_sp-1" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ gradient: "ld-top_from-green_to-00FF0010_sp-1" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient-ld-top_from-green_to-00FF0010_sp-1/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1`); - }); - - it('should generate the correct URL when gradient transformation is provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- gradient: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ gradient: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); - }); - - it('should generate the correct URL when gradient transformation is set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- gradient: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ gradient: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-gradient/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient`); - }); - - it('should generate the correct URL when AI drop shadow transformation is set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiDropShadow: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiDropShadow: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); - }); - - it('should generate the correct URL when AI drop shadow transformation is provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiDropShadow: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiDropShadow: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow`); - }); - - it('should generate the correct URL when AI drop shadow transformation is provided with a specific string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aiDropShadow: "az-45" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aiDropShadow: "az-45" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-dropshadow-az-45/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-az-45`); - }); - - it('should generate the correct URL when shadow transformation is set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- shadow: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ shadow: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); - }); - - it('should generate the correct URL when shadow transformation is provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- shadow: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ shadow: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow`); - }); - - it('should generate the correct URL when shadow transformation is provided with a specific string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- shadow: "bl-15_st-40_x-10_y-N5" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ shadow: "bl-15_st-40_x-10_y-N5" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-shadow-bl-15_st-40_x-10_y-N5/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-shadow-bl-15_st-40_x-10_y-N5`); - }); - - it('should generate the correct URL when sharpen transformation is set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- sharpen: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ sharpen: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); - }); - - it('should generate the correct URL when sharpen transformation is provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- sharpen: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ sharpen: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen`); - }); - - it('should generate the correct URL when sharpen transformation is provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- sharpen: 10 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ sharpen: 10 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-sharpen-10/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-sharpen-10`); - }); - - it('should generate the correct URL when unsharpMask transformation is set to true', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- unsharpMask: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ unsharpMask: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); - }); - - it('should generate the correct URL when unsharpMask transformation is provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- unsharpMask: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ unsharpMask: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm`); - }); - - it('should generate the correct URL when unsharpMask transformation is provided with a string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- unsharpMask: "2-2-0.8-0.024" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ unsharpMask: "2-2-0.8-0.024" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:e-usm-2-2-0.8-0.024/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-usm-2-2-0.8-0.024`); - }); - - it('should generate the correct URL for trim transformation when set to true (boolean)', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- trim: true -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ trim: true -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); - }); - - it('should generate the correct URL for trim transformation when provided as an empty string', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- trim: "" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ trim: "" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-true/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true`); - }); - - it('should generate the correct URL for trim transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- trim: 5 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ trim: 5 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:t-5/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-5`); - }); - - // Width parameter tests - it('should generate the correct URL for width transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- width: 400 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ width: 400 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); - }); - - it('should generate the correct URL for width transformation when provided with a string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- width: "400" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ width: "400" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-400/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400`); - }); - - it('should generate the correct URL for width transformation when provided with an arithmetic expression', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- width: "iw_div_2" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ width: "iw_div_2" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:w-iw_div_2/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-iw_div_2`); - }); - - // Height parameter tests - it('should generate the correct URL for height transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- height: 300 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ height: 300 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); - }); - - it('should generate the correct URL for height transformation when provided with a string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- height: "300" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ height: "300" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-300/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300`); - }); - - it('should generate the correct URL for height transformation when provided with an arithmetic expression', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- height: "ih_mul_0.5" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ height: "ih_mul_0.5" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:h-ih_mul_0.5/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-ih_mul_0.5`); - }); - - // AspectRatio parameter tests - it('should generate the correct URL for aspectRatio transformation when provided with a string value in colon format', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aspectRatio: "4:3" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aspectRatio: "4:3" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4:3/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3`); - }); - - it('should generate the correct URL for aspectRatio transformation when provided with an alternate underscore format', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aspectRatio: "4_3" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aspectRatio: "4_3" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-4_3/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4_3`); - }); - - it('should generate the correct URL for aspectRatio transformation when provided with an arithmetic expression', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- aspectRatio: "iar_div_2" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ aspectRatio: "iar_div_2" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:ar-iar_div_2/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-iar_div_2`); - }); - - // Background parameter tests - it('should generate the correct URL for background transformation when provided with a solid color', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- background: "FF0000" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ background: "FF0000" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-FF0000/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-FF0000`); - }); - - it('should generate the correct URL for background transformation when provided with the blurred option', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- background: "blurred" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ background: "blurred" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-blurred/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-blurred`); - }); - - it('should generate the correct URL for background transformation when provided with the genfill option', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- background: "genfill" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ background: "genfill" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:bg-genfill/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=bg-genfill`); - }); - - // Crop parameter tests - it('should generate the correct URL for crop transformation when provided with force value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- crop: "force" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ crop: "force" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-force/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-force`); - }); - - it('should generate the correct URL for crop transformation when provided with at_max value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- crop: "at_max" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ crop: "at_max" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:c-at_max/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=c-at_max`); - }); - - // CropMode parameter tests - it('should generate the correct URL for cropMode transformation when provided with pad_resize', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- cropMode: "pad_resize" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ cropMode: "pad_resize" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-pad_resize/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-pad_resize`); - }); - - it('should generate the correct URL for cropMode transformation when provided with extract value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- cropMode: "extract" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ cropMode: "extract" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:cm-extract/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=cm-extract`); - }); - - // Focus parameter tests - it('should generate the correct URL for focus transformation when provided with a string value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- focus: "center" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ focus: "center" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-center/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-center`); - }); - - it('should generate the correct URL for focus transformation when face detection is specified', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- focus: "face" -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ focus: "face" -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:fo-face/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=fo-face`); - }); - - // Quality parameter test - it('should generate the correct URL for quality transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- quality: 80 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ quality: 80 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:q-80/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80`); - }); - - // Coordinate parameters tests - it('should generate the correct URL for x coordinate transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- x: 10 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ x: 10 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:x-10/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=x-10`); - }); - - it('should generate the correct URL for y coordinate transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- y: 20 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ y: 20 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:y-20/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=y-20`); - }); - - it('should generate the correct URL for xCenter transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- xCenter: 30 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ xCenter: 30 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:xc-30/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=xc-30`); - }); - - it('should generate the correct URL for yCenter transformation when provided with a number value', function () { -- const url = imagekit.url({ -- path: "/test_path1.jpg", -- transformation: [{ -- yCenter: 40 -- }] -- }) -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path1.jpg", -+ transformation: [ -+ { -+ yCenter: 40 -+ } -+ ] -+ }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:yc-40/test_path1.jpg`); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=yc-40`); - }); - -- // This is done just to test how SDK constructs URL, the actual transformation is not valid. - it('Including deprecated properties', 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, -- sharpen: 10, -- unsharpMask: "2-2-0.8-0.024", -- contrastStretch: true, -- grayscale: true, -- shadow: 'bl-15_st-40_x-10_y-N5', -- gradient: 'from-red_to-white', -- original: true, -- raw: "h-200,w-300,l-image,i-logo.png,l-end" -- }] -- }) -- -- expect(url).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,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); -- }); -- -- // This is done just to test how SDK constructs URL, the actual transformation is not valid -- it('should generate the correct URL when comprehensive transformations, including video and AI transformations, are applied', 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, -- x: 10, -- y: 20, -- xCenter: 30, -- yCenter: 40, -- flip: "h", -- opacity: 0.8, -- zoom: 2, -- // Video transformations -- videoCodec: "h264", -- audioCodec: "aac", -- startOffset: 5, -- endOffset: 15, -- duration: 10, -- streamingResolutions: ["1440", "1080"], -- // AI transformations -- grayscale: true, -- aiUpscale: true, -- aiRetouch: true, -- aiVariation: true, -- aiDropShadow: true, -- aiChangeBackground: "prompt-car", -- aiRemoveBackground: true, -- contrastStretch: true, -- shadow: 'bl-15_st-40_x-10_y-N5', -- sharpen: 10, -- unsharpMask: "2-2-0.8-0.024", -- gradient: 'from-red_to-white', -- original: true, -- page: "2_4", -- raw: "h-200,w-300,l-image,i-logo.png,l-end" -- }] -- }) -- -- expect(url).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,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end/test_path.jpg`); -+ // This is just testing how the SDK constructs the URL, not actual valid transformations. -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: 300, -+ width: 400, -+ aspectRatio: '4-3', -+ quality: 40, -+ crop: 'force', -+ cropMode: 'extract', -+ focus: 'left', -+ format: 'jpeg', -+ radius: 50, -+ 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/", -+ dpr: 3, -+ sharpen: 10, -+ unsharpMask: "2-2-0.8-0.024", -+ contrastStretch: true, -+ grayscale: true, -+ shadow: "bl-15_st-40_x-10_y-N5", -+ gradient: "from-red_to-white", -+ original: true, -+ raw: "h-200,w-300,l-image,i-logo.png,l-end" -+ } -+ ] -+ }); -+ -+ expect(url).equal( -+ `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,e-sharpen-10,e-usm-2-2-0.8-0.024,e-contrast,e-grayscale,e-shadow-bl-15_st-40_x-10_y-N5,e-gradient-from-red_to-white,orig-true,h-200,w-300,l-image,i-logo.png,l-end` -+ ); -+ }); -+ -+ it('should generate the correct URL with many transformations, including video and AI transforms', function () { -+ // Example test with comprehensive transformations -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ transformationPosition: "query", -+ src: "/test_path.jpg", -+ transformation: [ -+ { -+ height: 300, -+ width: 400, -+ aspectRatio: '4-3', -+ quality: 40, -+ crop: 'force', -+ cropMode: 'extract', -+ focus: 'left', -+ format: 'jpeg', -+ radius: 50, -+ 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/", -+ dpr: 3, -+ x: 10, -+ y: 20, -+ xCenter: 30, -+ yCenter: 40, -+ flip: "h", -+ opacity: 0.8, -+ zoom: 2, -+ // Video transformations -+ videoCodec: "h264", -+ audioCodec: "aac", -+ startOffset: 5, -+ endOffset: 15, -+ duration: 10, -+ streamingResolutions: ["1440", "1080"], -+ // AI transformations -+ grayscale: true, -+ aiUpscale: true, -+ aiRetouch: true, -+ aiVariation: true, -+ aiDropShadow: true, -+ aiChangeBackground: "prompt-car", -+ aiRemoveBackground: true, -+ contrastStretch: true, -+ shadow: "bl-15_st-40_x-10_y-N5", -+ sharpen: 10, -+ unsharpMask: "2-2-0.8-0.024", -+ gradient: "from-red_to-white", -+ original: true, -+ page: "2_4", -+ raw: "h-200,w-300,l-image,i-logo.png,l-end" -+ } -+ ] -+ }); -+ -+ expect(url).equal( -+ `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end` -+ ); - }); - }); -diff --git a/test/url-generation/buildtransformationString.js b/test/url-generation/buildtransformationString.js -new file mode 100644 -index 0000000..f116b2e ---- /dev/null -+++ b/test/url-generation/buildtransformationString.js -@@ -0,0 +1,26 @@ -+const { buildTransformationString } = require("../../src/index"); -+const { expect } = require('chai'); -+ -+describe('buildTransformationString', function () { -+ it('should return an empty string when no transformations are provided', function () { -+ const result = buildTransformationString([{}]); -+ expect(result).to.equal(''); -+ }); -+ -+ it('should generate a transformation string for width only', function () { -+ const result = buildTransformationString([{ width: 300 }]); -+ expect(result).to.equal('w-300'); -+ }); -+ -+ it('should generate a transformation string for multiple transformations', function () { -+ const result = buildTransformationString([ -+ { -+ overlay: { -+ type: 'text', -+ text: 'Hello', -+ } -+ } -+ ]); -+ expect(result).to.equal('l-text,i-Hello,l-end'); -+ }); -+}); -diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js -index 52f67b4..0ade645 100644 ---- a/test/url-generation/overlay.js -+++ b/test/url-generation/overlay.js -@@ -1,14 +1,15 @@ - const chai = require("chai"); - const expect = chai.expect; --const initializationParams = require("../data").initializationParams; --import ImageKit from "../../src/index"; -+import { buildSrc } from "../../src/index"; - import { safeBtoa } from "../../src/utils/transformation"; -+ - describe("Overlay Transformation Test Cases", function () { -- const imagekit = new ImageKit(initializationParams); - - it('Ignore invalid values if text is missing', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "text" -@@ -18,9 +19,24 @@ describe("Overlay Transformation Test Cases", function () { - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); - }); - -- it('Ignore invalid values if input', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ it('Ignore if type is missing', function () { -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", -+ transformation: [{ -+ overlay: { -+ } -+ }] -+ }); -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); -+ }); -+ -+ it('Ignore invalid values if input (image)', function () { -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "image" -@@ -30,9 +46,11 @@ describe("Overlay Transformation Test Cases", function () { - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); - }); - -- it('Ignore invalid values if input', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ it('Ignore invalid values if input (video)', function () { -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "video" -@@ -42,9 +60,11 @@ describe("Overlay Transformation Test Cases", function () { - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); - }); - -- it('Ignore invalid values if input', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ it('Ignore invalid values if input (subtitle)', function () { -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "subtitle" -@@ -54,9 +74,11 @@ describe("Overlay Transformation Test Cases", function () { - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/base-image.jpg`); - }); - -- it('Ignore invalid values if color is missing', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ it('Ignore invalid values if color is missing (solidColor)', function () { -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "solidColor" -@@ -67,8 +89,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it('Text overlay generates correct URL with encoded overlay text', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -80,8 +104,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it('Image overlay generates correct URL with input logo.png', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "image", -@@ -93,8 +119,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it('Video overlay generates correct URL with input play-pause-loop.mp4', function () { -- const url = imagekit.url({ -- path: "/base-video.mp4", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-video.mp4", - transformation: [{ - overlay: { - type: "video", -@@ -106,8 +134,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it("Subtitle overlay generates correct URL with input subtitle.srt", function () { -- const url = imagekit.url({ -- path: "/base-video.mp4", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-video.mp4", - transformation: [{ - overlay: { - type: "subtitle", -@@ -119,8 +149,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it("Solid color overlay generates correct URL with background color FF0000", function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [{ - overlay: { - type: "solidColor", -@@ -132,8 +164,10 @@ describe("Overlay Transformation Test Cases", function () { - }); - - it('Combined overlay transformations generate correct URL including nested overlays', function () { -- const url = imagekit.url({ -- path: "/base-image.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", -+ src: "/base-image.jpg", - transformation: [ - { - // Text overlay -@@ -197,7 +231,7 @@ describe("Overlay Transformation Test Cases", function () { - } - }, - { -- // Video overlay. Just for url generation testing, you can't overlay a video on an image. -+ // Video overlay. Just for URL generation testing, you can't actually overlay a video on an image. - overlay: { - type: "video", - input: "play-pause-loop.mp4", -@@ -220,7 +254,7 @@ describe("Overlay Transformation Test Cases", function () { - } - }, - { -- // Subtitle overlay. Just for url generation testing, you can't overlay a subtitle on an image. -+ // Subtitle overlay. Just for URL generation testing, you can't actually overlay a subtitle on an image. - overlay: { - type: "subtitle", - input: "subtitle.srt", -@@ -268,20 +302,17 @@ describe("Overlay Transformation Test Cases", function () { - ] - }); - -- expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`) -+ expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`); - }); - }); - -- - describe("Overlay encoding test cases", function () { -- const imagekit = new ImageKit({ -- ...initializationParams, -- urlEndpoint: "https://ik.imagekit.io/demo", // Using real url to test correctness quickly by clicking link -- }); -- - it('Nested simple path, should use i instead of ie, handle slash properly', function () { -- const url = imagekit.url({ -- path: "/medium_cafe_B1iTdD0C.jpg", -+ const url = buildSrc({ -+ // Using a different endpoint here, as we are checking for /demo -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/medium_cafe_B1iTdD0C.jpg", - transformation: [{ - overlay: { - type: "image", -@@ -293,8 +324,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Nested non-simple path, should use ie instead of i', function () { -- const url = imagekit.url({ -- path: "/medium_cafe_B1iTdD0C.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/medium_cafe_B1iTdD0C.jpg", - transformation: [{ - overlay: { - type: "image", -@@ -306,8 +339,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Simple text overlay, should use i instead of ie', function () { -- const url = imagekit.url({ -- path: "/medium_cafe_B1iTdD0C.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/medium_cafe_B1iTdD0C.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -319,8 +354,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Simple text overlay with spaces and other safe characters, should use i instead of ie', function () { -- const url = imagekit.url({ -- path: "/medium_cafe_B1iTdD0C.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/medium_cafe_B1iTdD0C.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -332,8 +369,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Non simple text overlay, should use ie instead of i', function () { -- const url = imagekit.url({ -- path: "/medium_cafe_B1iTdD0C.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/medium_cafe_B1iTdD0C.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -345,8 +384,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Text overlay with explicit plain encoding', function () { -- const url = imagekit.url({ -- path: "/sample.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -359,8 +400,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Text overlay with explicit base64 encoding', function () { -- const url = imagekit.url({ -- path: "/sample.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.jpg", - transformation: [{ - overlay: { - type: "text", -@@ -373,8 +416,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Image overlay with explicit plain encoding', function () { -- const url = imagekit.url({ -- path: "/sample.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.jpg", - transformation: [{ - overlay: { - type: "image", -@@ -387,8 +432,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Image overlay with explicit base64 encoding', function () { -- const url = imagekit.url({ -- path: "/sample.jpg", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.jpg", - transformation: [{ - overlay: { - type: "image", -@@ -401,8 +448,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Video overlay with explicit base64 encoding', function () { -- const url = imagekit.url({ -- path: "/sample.mp4", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.mp4", - transformation: [{ - overlay: { - type: "video", -@@ -415,8 +464,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Subtitle overlay with explicit plain encoding', function () { -- const url = imagekit.url({ -- path: "/sample.mp4", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.mp4", - transformation: [{ - overlay: { - type: "subtitle", -@@ -429,8 +480,10 @@ describe("Overlay encoding test cases", function () { - }); - - it('Subtitle overlay with explicit base64 encoding', function () { -- const url = imagekit.url({ -- path: "/sample.mp4", -+ const url = buildSrc({ -+ transformationPosition: "path", -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.mp4", - transformation: [{ - overlay: { - type: "subtitle", -@@ -443,8 +496,9 @@ describe("Overlay encoding test cases", function () { - }); - - it("Avoid double encoding when transformation string is in query params", function () { -- const url = imagekit.url({ -- path: "/sample.jpg", -+ const url = buildSrc({ -+ urlEndpoint: "https://ik.imagekit.io/demo", -+ src: "/sample.jpg", - transformation: [{ - overlay: { - type: "text", From 8e0d74b9209969fa72c140beab64d4833e78bfd7 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:25:49 +0530 Subject: [PATCH 147/166] fix: remove gzip and brotli size badges from README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index dfe276c..2e5c86e 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ # ImageKit.io JavaScript SDK -![gzip size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=gzip&label=gzip) -![brotli size](https://img.badgesize.io/https://unpkg.com/@imagekit/javascript/dist/imagekit.min.js?compression=brotli&label=brotli) ![Node CI](https://github.com/imagekit-developer/imagekit-javascript/workflows/Node%20CI/badge.svg) [![npm version](https://img.shields.io/npm/v/@imagekit/javascript)](https://www.npmjs.com/package/@imagekit/javascript) [![codecov](https://codecov.io/gh/imagekit-developer/imagekit-javascript/branch/master/graph/badge.svg)](https://codecov.io/gh/imagekit-developer/imagekit-javascript) From 48c569177daad9a2982d9b181dea60c631b6a4cc Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 11 Apr 2025 15:32:10 +0530 Subject: [PATCH 148/166] fix: remove beta tag from publishConfig in package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index b64f91a..8d007cb 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "startSampleApp": "yarn build && cd samples/sample-app/ && yarn install && node index.js" }, "publishConfig": { - "tag": "beta", "access": "public" }, "repository": { From 3f5ce73016ab8cb3d6b677ab6f44fd781672cd61 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 15:39:49 +0530 Subject: [PATCH 149/166] feat: add getImageProps function for responsive image handling --- src/getImageProps.ts | 102 +++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/getImageProps.ts diff --git a/src/getImageProps.ts b/src/getImageProps.ts new file mode 100644 index 0000000..4903ba2 --- /dev/null +++ b/src/getImageProps.ts @@ -0,0 +1,102 @@ +import { buildSrc } from './url' +import type { SrcOptions } from './interfaces' + +const DEVICE_SIZES = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const +const IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const + +export interface ResponsiveOptions extends SrcOptions { + width?: number + sizes?: string + deviceSizes?: number[] + imageSizes?: number[] +} + +export interface ImageProps { + src: string + srcSet?: string + sizes?: string + width?: number + height?: number +} + +export function getImageProps(opts: ResponsiveOptions): ImageProps { + const { + src, + urlEndpoint, + transformation = [], + queryParameters, + transformationPosition, + sizes, + width, + deviceSizes = DEVICE_SIZES as unknown as number[], + imageSizes = IMAGE_SIZES as unknown as number[], + } = opts + + const allBreakpoints = [...imageSizes, ...deviceSizes].sort((a, b) => a - b) + + const { widths, kind } = pickWidths({ + all: allBreakpoints, + device: deviceSizes, + explicit: width, + sizesAttr: sizes, + }) + + const build = (w: number) => + buildSrc({ + src, + urlEndpoint, + queryParameters, + transformationPosition, + transformation: [ + ...transformation, + { width: w, crop: "at_max" } // Should never upscale beyond the original width + ], + }) + + const srcSet = + widths + .map((w, i) => `${build(w)} ${kind === 'w' ? w : i + 1}${kind}`) + .join(', ') || undefined + + return { + sizes: sizes ?? (kind === 'w' ? '100vw' : undefined), + srcSet, + src: build(widths[widths.length - 1]), + width, + } +} + +function pickWidths({ + all, + device, + explicit, + sizesAttr, +}: { + all: number[] + device: number[] + explicit?: number + sizesAttr?: string +}): { widths: number[]; kind: 'w' | 'x' } { + if (sizesAttr) { + const vwMatches = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] + const percents = vwMatches.map((m) => parseInt(m, 10)) + + if (percents.length) { + const smallest = Math.min(...percents) / 100 + const cutOff = device[0] * smallest + return { widths: all.filter((w) => w >= cutOff), kind: 'w' } + } + + return { widths: all, kind: 'w' } // ← return allSizes when no vw tokens + } + + // If sizes is not defined, we need to check if the explicit width is defined. If no width is defined, we can use the deviceSizes as the default. + if (typeof explicit !== 'number') { + return { widths: device, kind: 'w' } + } + + const nearest = (t: number) => + all.find((v) => v >= t) || all[all.length - 1] + const list = Array.from(new Set([nearest(explicit), nearest(explicit * 2)])) + return { widths: list, kind: 'x' } +} diff --git a/src/index.ts b/src/index.ts index 4b047e1..46bd971 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,9 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; +import { getImageProps } from "./getImageProps"; -export { buildSrc, buildTransformationString, upload, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; +export { buildSrc, buildTransformationString, upload, getImageProps, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, From 83d38b12936e1764d546a5c85d9f7e902d228546 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:05:29 +0530 Subject: [PATCH 150/166] feat: implement getResponsiveImageAttributes function and corresponding tests --- src/getImageProps.ts | 102 ----------------- src/getResponsiveImageAttributes.ts | 112 +++++++++++++++++++ src/index.ts | 6 +- test/getResponsiveImageAttributes.test.js | 130 ++++++++++++++++++++++ 4 files changed, 244 insertions(+), 106 deletions(-) delete mode 100644 src/getImageProps.ts create mode 100644 src/getResponsiveImageAttributes.ts create mode 100644 test/getResponsiveImageAttributes.test.js diff --git a/src/getImageProps.ts b/src/getImageProps.ts deleted file mode 100644 index 4903ba2..0000000 --- a/src/getImageProps.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { buildSrc } from './url' -import type { SrcOptions } from './interfaces' - -const DEVICE_SIZES = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const -const IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const - -export interface ResponsiveOptions extends SrcOptions { - width?: number - sizes?: string - deviceSizes?: number[] - imageSizes?: number[] -} - -export interface ImageProps { - src: string - srcSet?: string - sizes?: string - width?: number - height?: number -} - -export function getImageProps(opts: ResponsiveOptions): ImageProps { - const { - src, - urlEndpoint, - transformation = [], - queryParameters, - transformationPosition, - sizes, - width, - deviceSizes = DEVICE_SIZES as unknown as number[], - imageSizes = IMAGE_SIZES as unknown as number[], - } = opts - - const allBreakpoints = [...imageSizes, ...deviceSizes].sort((a, b) => a - b) - - const { widths, kind } = pickWidths({ - all: allBreakpoints, - device: deviceSizes, - explicit: width, - sizesAttr: sizes, - }) - - const build = (w: number) => - buildSrc({ - src, - urlEndpoint, - queryParameters, - transformationPosition, - transformation: [ - ...transformation, - { width: w, crop: "at_max" } // Should never upscale beyond the original width - ], - }) - - const srcSet = - widths - .map((w, i) => `${build(w)} ${kind === 'w' ? w : i + 1}${kind}`) - .join(', ') || undefined - - return { - sizes: sizes ?? (kind === 'w' ? '100vw' : undefined), - srcSet, - src: build(widths[widths.length - 1]), - width, - } -} - -function pickWidths({ - all, - device, - explicit, - sizesAttr, -}: { - all: number[] - device: number[] - explicit?: number - sizesAttr?: string -}): { widths: number[]; kind: 'w' | 'x' } { - if (sizesAttr) { - const vwMatches = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] - const percents = vwMatches.map((m) => parseInt(m, 10)) - - if (percents.length) { - const smallest = Math.min(...percents) / 100 - const cutOff = device[0] * smallest - return { widths: all.filter((w) => w >= cutOff), kind: 'w' } - } - - return { widths: all, kind: 'w' } // ← return allSizes when no vw tokens - } - - // If sizes is not defined, we need to check if the explicit width is defined. If no width is defined, we can use the deviceSizes as the default. - if (typeof explicit !== 'number') { - return { widths: device, kind: 'w' } - } - - const nearest = (t: number) => - all.find((v) => v >= t) || all[all.length - 1] - const list = Array.from(new Set([nearest(explicit), nearest(explicit * 2)])) - return { widths: list, kind: 'x' } -} diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts new file mode 100644 index 0000000..3752121 --- /dev/null +++ b/src/getResponsiveImageAttributes.ts @@ -0,0 +1,112 @@ +import type { SrcOptions } from './interfaces' +import { buildSrc } from './url' + +/* Default break‑point pools */ +const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const +const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const + +export interface GetImageAttributesOptions extends SrcOptions { + width?: number // explicit rendered width + sizes?: string // the HTML sizes value + deviceBreakpoints?: number[] // override default device break‑points + imageBreakpoints?: number[] // override tiny image break‑points +} + +export interface ResponsiveImageAttributes { + src: string + srcSet?: string + sizes?: string + width?: number +} + +export function getResponsiveImageAttributes( + opts: GetImageAttributesOptions +): ResponsiveImageAttributes { + const { + src, + urlEndpoint, + transformation = [], + queryParameters, + transformationPosition, + sizes, + width, + deviceBreakpoints = DEFAULT_DEVICE_BREAKPOINTS as unknown as number[], + imageBreakpoints = DEFAULT_IMAGE_BREAKPOINTS as unknown as number[], + } = opts + + const allBreakpoints = [...imageBreakpoints, ...deviceBreakpoints].sort((a, b) => a - b) + + const { candidates, descriptorKind } = computeCandidateWidths({ + allBreakpoints, + deviceBreakpoints, + explicitWidth: width, + sizesAttr: sizes, + }) + + /* helper to build a single ImageKit URL */ + const buildURL = (w: number) => + buildSrc({ + src, + urlEndpoint, + queryParameters, + transformationPosition, + transformation: [ + ...transformation, + { width: w, crop: 'at_max' }, // never upscale beyond original + ], + }) + + /* build srcSet */ + const srcSet = + candidates + .map((w, i) => `${buildURL(w)} ${descriptorKind === 'w' ? w : i + 1}${descriptorKind}`) + .join(', ') || undefined + + return { + src: buildURL(candidates[candidates.length - 1]), // largest candidate + srcSet, + sizes: sizes ?? (descriptorKind === 'w' ? '100vw' : undefined), + width, + } +} + +function computeCandidateWidths(params: { + allBreakpoints: number[] + deviceBreakpoints: number[] + explicitWidth?: number + sizesAttr?: string +}): { candidates: number[]; descriptorKind: 'w' | 'x' } { + const { allBreakpoints, deviceBreakpoints, explicitWidth, sizesAttr } = params + + /* --- sizes attribute present ----------------------------------- */ + if (sizesAttr) { + const vwTokens = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] + const vwPercents = vwTokens.map((t) => parseInt(t, 10)) + + if (vwPercents.length) { + const smallestRatio = Math.min(...vwPercents) / 100 + const minRequiredPx = deviceBreakpoints[0] * smallestRatio + return { + candidates: allBreakpoints.filter((w) => w >= minRequiredPx), + descriptorKind: 'w', + } + } + /* no vw → give the full break‑point list */ + return { candidates: allBreakpoints, descriptorKind: 'w' } + } + + /* --- no sizes attr ------------------------------------------------ */ + if (typeof explicitWidth !== 'number') { + return { candidates: deviceBreakpoints, descriptorKind: 'w' } + } + + /* DPR strategy: 1× & 2× nearest break‑points */ + const nearest = (t: number) => + allBreakpoints.find((n) => n >= t) || allBreakpoints[allBreakpoints.length - 1] + + const unique = Array.from( + new Set([nearest(explicitWidth), nearest(explicitWidth * 2)]), + ) + + return { candidates: unique, descriptorKind: 'x' } +} diff --git a/src/index.ts b/src/index.ts index 46bd971..321c824 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,12 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; -import { getImageProps } from "./getImageProps"; +import { getResponsiveImageAttributes } from "./getResponsiveImageAttributes"; -export { buildSrc, buildTransformationString, upload, getImageProps, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; +export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, UploadOptions, UploadResponse }; - - diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js new file mode 100644 index 0000000..b6a4db8 --- /dev/null +++ b/test/getResponsiveImageAttributes.test.js @@ -0,0 +1,130 @@ +const { expect } = require('chai'); +const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); + +describe.only('getResponsiveImageAttributes – smoke run‑through', () => { + it('bare minimum input', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + }); + // Expected object based on default deviceSizes and imageSizes: + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100vw", + width: undefined + }); + }); + + it('sizes provided (100vw)', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '100vw', + }); + // With a sizes value of "100vw", the function should use the same breakpoints as in the bare minimum case. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100vw", + width: undefined + }); + }); + + it('width only – DPR strategy', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 400, + }); + // When width is provided without sizes attribute, the DPR strategy should be used. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 2x", + sizes: undefined, + width: 400 + }); + }); + + it('custom breakpoints', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + deviceSizes: [200, 400, 800], + imageSizes: [100], + }); + // For custom breakpoints, the breakpoints will be derived from the provided arrays. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-200,c-at_max 200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max 400w, https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max 800w", + sizes: "100vw", + width: undefined + }); + }); + + it('preserves caller transformations', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 500, + transformation: [{ height: 300 }], + }); + // The provided transformation should be preserved in the output. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max 2x", + sizes: undefined, + width: 500 + }); + }); + + it('both sizes and width passed', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '50vw', + width: 600, + }); + // Both sizes and width are provided, so the function should apply the sizes attribute while using width for DPR strategy. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "50vw", + width: 600 + }); + }); + + it('multiple transformations', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 450, + transformation: [ + { height: 300 }, + { aiRemoveBackground: true } + ] + }); + // Multiple caller transformations should be combined appropriately. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max 2x", + sizes: undefined, + width: 450 + }); + }); + + it('sizes causes breakpoint pruning (33vw path)', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '(min-width: 800px) 33vw, 100vw', + }); + // When specified with a sizes attribute that prunes breakpoints, the output should reflect the pruned values. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "(min-width: 800px) 33vw, 100vw", + width: undefined + }); + }); +}); From b7a5758843433427e445b679cd26a341a9e1f441 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:16:58 +0530 Subject: [PATCH 151/166] fix test cases --- src/getResponsiveImageAttributes.ts | 8 +++-- test/getResponsiveImageAttributes.test.js | 41 ++++++++++------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 3752121..5abf1e3 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -3,7 +3,7 @@ import { buildSrc } from './url' /* Default break‑point pools */ const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const -const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const +const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { width?: number // explicit rendered width @@ -62,11 +62,13 @@ export function getResponsiveImageAttributes( .map((w, i) => `${buildURL(w)} ${descriptorKind === 'w' ? w : i + 1}${descriptorKind}`) .join(', ') || undefined + const finalSizes = sizes ?? (descriptorKind === 'w' ? '100vw' : undefined) + return { src: buildURL(candidates[candidates.length - 1]), // largest candidate srcSet, - sizes: sizes ?? (descriptorKind === 'w' ? '100vw' : undefined), - width, + ...(finalSizes ? { sizes: finalSizes } : {}), // include only when defined + ...(width !== undefined ? { width } : {}), // include only when defined } } diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index b6a4db8..9f6aa67 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -1,18 +1,17 @@ const { expect } = require('chai'); const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); -describe.only('getResponsiveImageAttributes – smoke run‑through', () => { +describe.only('getResponsiveImageAttributes', () => { it('bare minimum input', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', urlEndpoint: 'https://ik.imagekit.io/demo', }); - // Expected object based on default deviceSizes and imageSizes: + // Expected object based on default deviceBreakpoints and imageBreakpoints: expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -26,8 +25,7 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -39,9 +37,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // When width is provided without sizes attribute, the DPR strategy should be used. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 2x", width: 400 }); }); @@ -50,15 +47,14 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', urlEndpoint: 'https://ik.imagekit.io/demo', - deviceSizes: [200, 400, 800], - imageSizes: [100], + deviceBreakpoints: [200, 400, 800], + imageBreakpoints: [100], }); // For custom breakpoints, the breakpoints will be derived from the provided arrays. expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-200,c-at_max 200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max 400w, https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max 800w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -71,9 +67,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // The provided transformation should be preserved in the output. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-1080,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-1080,c-at_max 2x", width: 500 }); }); @@ -87,8 +82,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // Both sizes and width are provided, so the function should apply the sizes attribute while using width for DPR strategy. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", sizes: "50vw", width: 600 }); @@ -106,9 +101,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // Multiple caller transformations should be combined appropriately. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-1080,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-1080,c-at_max 2x", width: 450 }); }); @@ -122,9 +116,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { // When specified with a sizes attribute that prunes breakpoints, the output should reflect the pruned values. expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "(min-width: 800px) 33vw, 100vw", - width: undefined + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max 256w, https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "(min-width: 800px) 33vw, 100vw" }); }); }); From 2ad35ebb39a43170218f771019cc2296de5a035f Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:31:27 +0530 Subject: [PATCH 152/166] add jsdocs and add test for query parameters --- src/getResponsiveImageAttributes.ts | 48 +++++++++++++++++++---- test/getResponsiveImageAttributes.test.js | 22 +++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 5abf1e3..731794d 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -6,12 +6,39 @@ const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { - width?: number // explicit rendered width - sizes?: string // the HTML sizes value - deviceBreakpoints?: number[] // override default device break‑points - imageBreakpoints?: number[] // override tiny image break‑points + /** + * The intended display width (in pixels) of the image on screen. + * Used for calculating `srcSet` with a pixel-density (DPR) strategy. + * If omitted, a width-based strategy using breakpoints will be applied. + */ + width?: number + + /** + * The `sizes` attribute for the image element. + * Typically used to indicate how the image will scale across different viewport sizes (e.g., "100vw"). + * Presence of `sizes` triggers a width-based `srcSet` strategy. + */ + sizes?: string + + /** + * An optional custom list of device width breakpoints (in pixels). + * If not specified, defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. + * Recommended to align with your target audience's common screen widths. + */ + deviceBreakpoints?: number[] + + /** + * An optional list of custom image breakpoints (in pixels). + * These are merged with the device breakpoints to compute the final list of candidate widths. + * Defaults to `[16, 32, 48, 64, 96, 128, 256, 384]`. + */ + imageBreakpoints?: number[] } +/** + * Resulting set of attributes suitable for an HTML `` element. + * Useful for enabling responsive image loading. + */ export interface ResponsiveImageAttributes { src: string srcSet?: string @@ -19,6 +46,10 @@ export interface ResponsiveImageAttributes { width?: number } +/** + * Generates a responsive image URL, `srcSet`, and `sizes` attributes + * based on the input options such as `width`, `sizes`, and breakpoints. + */ export function getResponsiveImageAttributes( opts: GetImageAttributesOptions ): ResponsiveImageAttributes { @@ -80,7 +111,7 @@ function computeCandidateWidths(params: { }): { candidates: number[]; descriptorKind: 'w' | 'x' } { const { allBreakpoints, deviceBreakpoints, explicitWidth, sizesAttr } = params - /* --- sizes attribute present ----------------------------------- */ + // Strategy 1: Width-based srcSet (`w`) using viewport `vw` hints if (sizesAttr) { const vwTokens = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] const vwPercents = vwTokens.map((t) => parseInt(t, 10)) @@ -93,16 +124,17 @@ function computeCandidateWidths(params: { descriptorKind: 'w', } } - /* no vw → give the full break‑point list */ + + // No usable `vw` found: fallback to all breakpoints return { candidates: allBreakpoints, descriptorKind: 'w' } } - /* --- no sizes attr ------------------------------------------------ */ + // Strategy 2: Fallback using explicit image width using device breakpoints if (typeof explicitWidth !== 'number') { return { candidates: deviceBreakpoints, descriptorKind: 'w' } } - /* DPR strategy: 1× & 2× nearest break‑points */ + // Strategy 3: Use 1x and 2x nearest breakpoints for `x` descriptor const nearest = (t: number) => allBreakpoints.find((n) => n >= t) || allBreakpoints[allBreakpoints.length - 1] diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 9f6aa67..75c4435 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -120,4 +120,26 @@ describe.only('getResponsiveImageAttributes', () => { sizes: "(min-width: 800px) 33vw, 100vw" }); }); + + it("Using queryParameters and transformationPosition", () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 450, + transformation: [ + { height: 300 }, + { aiRemoveBackground: true } + ], + queryParameters: { + key: "value" + }, + transformationPosition: "path" + }); + // The function should respect the transformation position and query parameters. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-1080,c-at_max/sample.jpg?key=value", + srcSet: "https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-640,c-at_max/sample.jpg?key=value 1x, https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-1080,c-at_max/sample.jpg?key=value 2x", + width: 450 + }); + }) }); From e543d8ef3776e83268fcb699aed64a4f34a92726 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:31:43 +0530 Subject: [PATCH 153/166] fix: remove .only from describe block in getResponsiveImageAttributes tests --- test/getResponsiveImageAttributes.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 75c4435..72da008 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -1,7 +1,7 @@ const { expect } = require('chai'); const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); -describe.only('getResponsiveImageAttributes', () => { +describe('getResponsiveImageAttributes', () => { it('bare minimum input', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', From a96d92526c3b9884613419f4a87757feffbd387d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:46:00 +0530 Subject: [PATCH 154/166] test: add fallback case for no usable vw tokens in getResponsiveImageAttributes --- test/getResponsiveImageAttributes.test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 72da008..28ad5da 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -142,4 +142,18 @@ describe('getResponsiveImageAttributes', () => { width: 450 }); }) + + it("fallback when no usable vw tokens", () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: "100%" + }); + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-16,c-at_max 16w, https://ik.imagekit.io/demo/sample.jpg?tr=w-32,c-at_max 32w, https://ik.imagekit.io/demo/sample.jpg?tr=w-48,c-at_max 48w, https://ik.imagekit.io/demo/sample.jpg?tr=w-64,c-at_max 64w, https://ik.imagekit.io/demo/sample.jpg?tr=w-96,c-at_max 96w, https://ik.imagekit.io/demo/sample.jpg?tr=w-128,c-at_max 128w, https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max 256w, https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100%" + }); + }) + }); From 2575dc46c5f58451fd32fde6b5e110c08fca7e22 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:51:19 +0530 Subject: [PATCH 155/166] fix: sort image breakpoints before merging with device breakpoints in getResponsiveImageAttributes --- src/getResponsiveImageAttributes.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 731794d..3240cdf 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -65,11 +65,13 @@ export function getResponsiveImageAttributes( imageBreakpoints = DEFAULT_IMAGE_BREAKPOINTS as unknown as number[], } = opts - const allBreakpoints = [...imageBreakpoints, ...deviceBreakpoints].sort((a, b) => a - b) + const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b); + const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b); + const allBreakpoints = [...sortedImageBreakpoints, ...sortedDeviceBreakpoints].sort((a, b) => a - b); const { candidates, descriptorKind } = computeCandidateWidths({ allBreakpoints, - deviceBreakpoints, + deviceBreakpoints: sortedDeviceBreakpoints, explicitWidth: width, sizesAttr: sizes, }) From a86f5159cb00ab5104340991f2e8d014b69d993c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 17:17:12 +0530 Subject: [PATCH 156/166] feat: add background and gradient properties to SolidColorOverlayTransformation type --- src/interfaces/Transformation.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index b45be9f..6396b37 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -720,4 +720,19 @@ export type SolidColorOverlayTransformation = Pick Date: Mon, 5 May 2025 17:17:23 +0530 Subject: [PATCH 157/166] refactor: enhance documentation for GetImageAttributesOptions and ResponsiveImageAttributes interfaces --- src/getResponsiveImageAttributes.ts | 40 ++++++++++++++++++++--------- src/index.ts | 4 ++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 3240cdf..3b3c120 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -7,30 +7,42 @@ const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { /** - * The intended display width (in pixels) of the image on screen. - * Used for calculating `srcSet` with a pixel-density (DPR) strategy. - * If omitted, a width-based strategy using breakpoints will be applied. - */ + * The intended display width of the image in pixels, + * used **only when the `sizes` attribute is not provided**. + * + * Triggers a DPR-based strategy (1x and 2x variants) and generates `x` descriptors in `srcSet`. + * + * Ignored if `sizes` is present. + */ width?: number /** - * The `sizes` attribute for the image element. - * Typically used to indicate how the image will scale across different viewport sizes (e.g., "100vw"). - * Presence of `sizes` triggers a width-based `srcSet` strategy. + * The value for the HTML `sizes` attribute + * (e.g., `"100vw"` or `"(min-width:768px) 50vw, 100vw"`). + * + * - If it includes one or more `vw` units, breakpoints smaller than the corresponding percentage of the smallest device width are excluded. + * - If it contains no `vw` units, the full breakpoint list is used. + * + * Enables a width-based strategy and generates `w` descriptors in `srcSet`. */ sizes?: string /** - * An optional custom list of device width breakpoints (in pixels). - * If not specified, defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. - * Recommended to align with your target audience's common screen widths. + * Custom list of **device-width breakpoints** in pixels. + * These define common screen widths for responsive image generation. + * + * Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. + * Sorted automatically. */ deviceBreakpoints?: number[] /** - * An optional list of custom image breakpoints (in pixels). - * These are merged with the device breakpoints to compute the final list of candidate widths. + * Custom list of **image-specific breakpoints** in pixels. + * Useful for generating small variants (e.g., placeholders or thumbnails). + * + * Merged with `deviceBreakpoints` before calculating `srcSet`. * Defaults to `[16, 32, 48, 64, 96, 128, 256, 384]`. + * Sorted automatically. */ imageBreakpoints?: number[] } @@ -40,9 +52,13 @@ export interface GetImageAttributesOptions extends SrcOptions { * Useful for enabling responsive image loading. */ export interface ResponsiveImageAttributes { + /** URL for the *largest* candidate (assigned to plain `src`). */ src: string + /** Candidate set with `w` or `x` descriptors. */ srcSet?: string + /** `sizes` returned (or synthesised as `100vw`). */ sizes?: string + /** Width as a number (if `width` was provided). */ width?: number } diff --git a/src/index.ts b/src/index.ts index 321c824..51ec6b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,13 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from " import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; import { getResponsiveImageAttributes } from "./getResponsiveImageAttributes"; +import type { GetImageAttributesOptions, ResponsiveImageAttributes } from "./getResponsiveImageAttributes"; export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, UploadOptions, - UploadResponse + UploadResponse, + GetImageAttributesOptions, ResponsiveImageAttributes }; From ec15969ad8233dea73407c32ddc404fc12569af0 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 17:25:34 +0530 Subject: [PATCH 158/166] chore: update version to 5.1.0-beta.1 and document new helper getResponsiveImageAttributes --- CHANGELOG.md | 9 +++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 220aee1..b184c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Version 5.1.0 + +1. **New helper** `getResponsiveImageAttributes()` + Generates ready‑to‑use `src`, `srcSet`, and `sizes` for responsive `` tags (breakpoint pruning, DPR 1×/2×, custom breakpoints, no up‑scaling). +2. Added exports: + `getResponsiveImageAttributes`, `GetImageAttributesOptions`, `ResponsiveImageAttributes`. + +_No breaking changes from 5.0.x._ + ## Version 5.0.0 This version introduces major breaking changes, for usage examples, refer to the [official documentation](https://imagekit.io/docs/integration/javascript). diff --git a/package-lock.json b/package-lock.json index aa14b58..cc7eafd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 8d007cb..f1d3e66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 1f56f437a8eaf7f1748989fe3e0ba3f53094f56d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Wed, 7 May 2025 16:26:53 +0530 Subject: [PATCH 159/166] feat: move getResponsiveImageAttributes and related types to responsive module --- src/index.ts | 4 ++-- src/{getResponsiveImageAttributes.ts => responsive.ts} | 0 test/{getResponsiveImageAttributes.test.js => responsive.js} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/{getResponsiveImageAttributes.ts => responsive.ts} (100%) rename test/{getResponsiveImageAttributes.test.js => responsive.js} (99%) diff --git a/src/index.ts b/src/index.ts index 51ec6b0..bf68707 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; -import { getResponsiveImageAttributes } from "./getResponsiveImageAttributes"; -import type { GetImageAttributesOptions, ResponsiveImageAttributes } from "./getResponsiveImageAttributes"; +import { getResponsiveImageAttributes } from "./responsive"; +import type { GetImageAttributesOptions, ResponsiveImageAttributes } from "./responsive"; export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { diff --git a/src/getResponsiveImageAttributes.ts b/src/responsive.ts similarity index 100% rename from src/getResponsiveImageAttributes.ts rename to src/responsive.ts diff --git a/test/getResponsiveImageAttributes.test.js b/test/responsive.js similarity index 99% rename from test/getResponsiveImageAttributes.test.js rename to test/responsive.js index 28ad5da..0f5397c 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/responsive.js @@ -1,5 +1,5 @@ const { expect } = require('chai'); -const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); +const { getResponsiveImageAttributes } = require("../src/index"); describe('getResponsiveImageAttributes', () => { it('bare minimum input', () => { From 211f031255e91506ad7169c6874a6fc54fc49bb8 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 9 May 2025 15:59:36 +0530 Subject: [PATCH 160/166] chore: update version from 5.1.0-beta.1 to 5.1.0 in package.json and package-lock.json --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cc7eafd..ffaffc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.1.0-beta.1", + "version": "5.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.1.0-beta.1", + "version": "5.1.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index f1d3e66..986fa4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.1.0-beta.1", + "version": "5.1.0", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 879e057035c3504271a6cd39c3ca17fda406c7fd Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 16 Jan 2026 14:41:32 +0530 Subject: [PATCH 161/166] feat: add layer mode support in overlay transformations and update URL generation tests - Enhanced the `processOverlay` function to include a new `layerMode` property for overlays. - Updated the URL generation tests to include new transformation parameters such as `aiEdit`, `colorReplace`, and `distort`. - Added comprehensive tests for various layer modes (multiply, cutter, cutout, displace) to ensure correct URL generation. --- src/constants/supportedTransforms.ts | 4 + src/interfaces/Transformation.ts | 957 ++++++++++++++++----------- src/url.ts | 7 +- test/url-generation/basic.js | 8 +- test/url-generation/overlay.js | 71 ++ 5 files changed, 651 insertions(+), 396 deletions(-) diff --git a/src/constants/supportedTransforms.ts b/src/constants/supportedTransforms.ts index 8353ceb..aee5fe3 100644 --- a/src/constants/supportedTransforms.ts +++ b/src/constants/supportedTransforms.ts @@ -39,6 +39,7 @@ export const supportedTransforms: { [key: string]: string } = { aiVariation: "e-genvar", aiDropShadow: "e-dropshadow", aiChangeBackground: "e-changebg", + aiEdit: "e-edit", aiRemoveBackground: "e-bgremove", aiRemoveBackgroundExternal: "e-removedotbg", contrastStretch: "e-contrast", @@ -46,6 +47,8 @@ export const supportedTransforms: { [key: string]: string } = { sharpen: "e-sharpen", unsharpMask: "e-usm", gradient: "e-gradient", + colorReplace: "cr", + distort: "e-distort", // Other flags & finishing progressive: "pr", @@ -56,6 +59,7 @@ export const supportedTransforms: { [key: string]: string } = { trim: "t", zoom: "z", page: "pg", + layerMode: "lm", // Text overlay transformations which are not defined yet fontSize: "fs", diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index 6396b37..f517855 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -1,507 +1,587 @@ +/** + * By default, the transformation string is added as a query parameter in the URL, + * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the + * path of the URL, set this to `path`. Learn more in the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ export type TransformationPosition = "path" | "query"; export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440" | "2160"; /** - * The SDK provides easy-to-use names for transformations. These names are converted to the corresponding transformation string before being added to the URL. - * SDKs are updated regularly to support new transformations. If you want to use a transformation that is not supported by the SDK, - * You can use the `raw` parameter to pass the transformation string directly. - * - * [Transformations Documentation](https://imagekit.io/docs/transformations) + * The SDK provides easy-to-use names for transformations. These names are + * converted to the corresponding transformation string before being added to the + * URL. SDKs are updated regularly to support new transformations. If you want to + * use a transformation that is not supported by the SDK, You can use the `raw` + * parameter to pass the transformation string directly. See the + * [Transformations documentation](https://imagekit.io/docs/transformations). */ export interface Transformation { /** - * Specifies the width of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.4` represents 40% of the original width). - * You can also supply arithmetic expressions (e.g., `iw_div_2`). - * - * Width transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) | [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) + * Uses AI to change the background. Provide a text prompt or a base64-encoded + * prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Not supported inside overlay. See + * [AI Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg). */ - width?: number | string; + aiChangeBackground?: string; /** - * Specifies the height of the output. If a value between 0 and 1 is provided, it is treated as a percentage (e.g., `0.5` represents 50% of the original height). - * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). - * - * Height transformation - [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) | [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) + * Adds an AI-based drop shadow around a foreground object on a transparent or + * removed background. Optionally, control the direction, elevation, and saturation + * of the light source (e.g., `az-45` to change light direction). Pass `true` for + * the default drop shadow, or provide a string for a custom drop shadow. Supported + * inside overlay. See + * [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). */ - height?: number | string; + aiDropShadow?: true | string; /** - * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with either width or height (but not both). - * For example: aspectRatio = `4:3`, `4_3`, or an expression like `iar_div_2`. - * - * [Image Resize and Crop - Aspect Ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar) + * Uses AI to edit images based on a text prompt. Provide a text prompt or a + * base64-encoded prompt, e.g., `prompt-snow road` or + * `prompte-[urlencoded_base64_encoded_text]`. Not supported inside overlay. + * See [AI Edit](https://imagekit.io/docs/ai-transformations#edit-image-e-edit). */ - aspectRatio?: number | string; + aiEdit?: string; /** - * Specifies the background to be used in conjunction with certain cropping strategies when resizing an image. - * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. - * - * [Effects and Enhancements - Solid Color Background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background) - * - * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. - * - * [Effects and Enhancements - Blurred Background](https://imagekit.io/docs/effects-and-enhancements#blurred-background) - * - * - Expand the image boundaries using generative fill: `genfill`. Not supported inside overlay. Optionally, control the background scene by passing a text prompt: - * `genfill[:-prompt-${text}]` or `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. - * - * [AI Transformations - Generative Fill Background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill) + * Applies ImageKit's in-house background removal. Supported inside overlay. See + * [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). */ - background?: string; + aiRemoveBackground?: true; /** - * Adds a border to the output media. Accepts a string in the format `_` - * (e.g., `5_FFF000` for a 5px yellow border), or an expression like `ih_div_20_FF00FF`. - * - * [Effects and Enhancements - Border](https://imagekit.io/docs/effects-and-enhancements#border---b) + * Uses third-party background removal. Note: It is recommended to use + * aiRemoveBackground, ImageKit's in-house solution, which is more cost-effective. + * Supported inside overlay. See + * [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). */ - border?: string; + aiRemoveBackgroundExternal?: true; /** - * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) + * Performs AI-based retouching to improve faces or product shots. Not supported + * inside overlay. See + * [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). */ - crop?: "force" | "at_max" | "at_max_enlarge" | "at_least" | "maintain_ratio"; + aiRetouch?: true; /** - * [Image Resize and Crop - Crop Modes](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus) + * Upscales images beyond their original dimensions using AI. Not supported inside + * overlay. See + * [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). */ - cropMode?: "pad_resize" | "extract" | "pad_extract"; + aiUpscale?: true; /** - * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio (DPR) calculation. - * - * [Image Resize and Crop - DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr) + * Generates a variation of an image using AI. This produces a new image with + * slight variations from the original, such as changes in color, texture, and + * other visual elements, while preserving the structure and essence of the + * original image. Not supported inside overlay. See + * [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). */ - dpr?: number + aiVariation?: true; /** - * This parameter can be used with pad resize, maintain ratio, or extract crop to modify the padding or cropping behavior. - * - * [Image Resize and Crop - Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo) + * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with + * either width or height (but not both). For example: aspectRatio = `4:3`, `4_3`, + * or an expression like `iar_div_2`. See + * [Image resize and crop – Aspect ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). */ - focus?: string; + aspectRatio?: number | string; /** - * Specifies the quality of the output image for lossy formats such as JPEG, WebP, and AVIF. - * A higher quality value results in a larger file size with better quality, while a lower value produces a smaller file size with reduced quality. - * - * [Image Optimization - Quality](https://imagekit.io/docs/image-optimization#quality---q) + * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See + * [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). */ - quality?: number; + audioCodec?: 'aac' | 'opus' | 'none'; /** - * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + * Specifies the background to be used in conjunction with certain cropping + * strategies when resizing an image. + * + * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. See + * [Solid color background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background). + * - Dominant color: `dominant` extracts the dominant color from the image. See + * [Dominant color background](https://imagekit.io/docs/effects-and-enhancements#dominant-color-background). + * - Gradient: `gradient_dominant` or `gradient_dominant_2` creates a gradient + * using the dominant colors. Optionally specify palette size (2 or 4), e.g., + * `gradient_dominant_4`. See + * [Gradient background](https://imagekit.io/docs/effects-and-enhancements#gradient-background). + * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. See + * [Blurred background](https://imagekit.io/docs/effects-and-enhancements#blurred-background). + * - Expand the image boundaries using generative fill: `genfill`. Not supported + * inside overlay. Optionally, control the background scene by passing a text + * prompt: `genfill[:-prompt-${text}]` or + * `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. See + * [Generative fill background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill). */ - x?: number | string; + background?: string; /** - * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, + * or an expression like `bl-10`. See + * [Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl). */ - xCenter?: number | string; + blur?: number; /** - * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + * Adds a border to the output media. Accepts a string in the format + * `_` (e.g., `5_FFF000` for a 5px yellow border), or an + * expression like `ih_div_20_FF00FF`. See + * [Border](https://imagekit.io/docs/effects-and-enhancements#border---b). */ - y?: number | string; + border?: string; /** - * [Image Resize and Crop - Focus Using Cropped Image Coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates) + * Indicates whether the output image should retain the original color profile. See + * [Color profile](https://imagekit.io/docs/image-optimization#color-profile---cp). */ - yCenter?: number | string; + colorProfile?: boolean; /** - * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, `mp4`, or `auto`. - * You can also pass `orig` for images to return the original format. - * ImageKit automatically delivers images and videos in the optimal format based on device support unless overridden by the dashboard settings or the format parameter. - * - * [Image Optimization - Format](https://imagekit.io/docs/image-optimization#format---f) & [Video Optimization - Format](https://imagekit.io/docs/video-optimization#format---f) + * Replaces colors in the image. Supports three formats: + * + * - `toColor` - Replace dominant color with the specified color. + * - `toColor_tolerance` - Replace dominant color with specified tolerance (0-100). + * - `toColor_tolerance_fromColor` - Replace a specific color with another within + * tolerance range. Colors can be hex codes (e.g., `FF0022`) or names (e.g., + * `red`, `blue`). See + * [Color replacement](https://imagekit.io/docs/effects-and-enhancements#color-replace---cr). */ - format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "gif" | "svg" | "mp4" | "webm" | "avif" | "orig"; + colorReplace?: string; /** - * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. - * - * [Video Optimization - Video Codec](https://imagekit.io/docs/video-optimization#video-codec---vc) + * Automatically enhances the contrast of an image (contrast stretch). See + * [Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). */ - videoCodec?: "h264" | "vp9" | "av1" | "none"; + contrastStretch?: true; /** - * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. - * - * [Video Optimization - Audio Codec](https://imagekit.io/docs/video-optimization#audio-codec---ac) + * Crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). */ - audioCodec?: "aac" | "opus" | "none"; + crop?: 'force' | 'at_max' | 'at_max_enlarge' | 'at_least' | 'maintain_ratio'; /** - * Specifies the corner radius for rounded corners (e.g., 20) or `max` for circular/oval shapes. - * - * [Effects and Enhancements - Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r) + * Additional crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). */ - radius?: number | "max"; + cropMode?: 'pad_resize' | 'extract' | 'pad_extract'; /** - * Specifies the rotation angle in degrees. Positive values rotate the image clockwise; you can also use, for example, `N40` for counterclockwise rotation - * or `auto` to use the orientation specified in the image's EXIF data. - * For videos, only the following values are supported: 0, 90, 180, 270, or 360. - * - * [Effects and Enhancements - Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt) + * Specifies a fallback image if the resource is not found, e.g., a URL or file + * path. See + * [Default image](https://imagekit.io/docs/image-transformation#default-image---di). */ - rotation?: number | string; + defaultImage?: string; /** - * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, or an expression like `bl-10`. - * - * [Effects and Enhancements - Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl) + * Distorts the shape of an image. Supports two modes: + * + * - Perspective distortion: `p-x1_y1_x2_y2_x3_y3_x4_y4` changes the position of + * the four corners starting clockwise from top-left. + * - Arc distortion: `a-degrees` curves the image upwards (positive values) or + * downwards (negative values). See + * [Distort effect](https://imagekit.io/docs/effects-and-enhancements#distort---e-distort). */ - blur?: number; + distort?: string; /** - * [Transformations - Named Transformations](https://imagekit.io/docs/transformations#named-transformations) + * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio + * (DPR) calculation. Also accepts arithmetic expressions. + * + * - Learn about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + * - See [DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr). */ - named?: string; + dpr?: number; /** - * Specifies a fallback image if the resource is not found, e.g., a URL or file path. - * - * [Image Transformation - Default Image](https://imagekit.io/docs/image-transformation#default-image---di) + * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to indicate the length from the start offset. + * Arithmetic expressions are supported. See + * [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). */ - defaultImage?: string; + duration?: number | string; /** - * Flips or mirrors an image either horizontally, vertically, or both. - * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. - * - * [Effects and Enhancements - Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl) + * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to define a time window. Arithmetic expressions + * are supported. See + * [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). */ - flip?: "h" | "v" | "h_v" | "v_h"; + endOffset?: number | string; /** - * If set to true, serves the original file without applying any transformations. - * - * [Core Delivery Features - Deliver Original File As Is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true) + * Flips or mirrors an image either horizontally, vertically, or both. Acceptable + * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or + * `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). */ - original?: boolean; + flip?: 'h' | 'v' | 'h_v' | 'v_h'; /** - * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Arithmetic expressions are also supported. - * - * [Trim Videos - Start Offset](https://imagekit.io/docs/trim-videos#start-offset---so) + * Refines padding and cropping behavior for pad resize, maintain ratio, and + * extract crop modes. Supports manual positions and coordinate-based focus. With + * AI-based cropping, you can automatically keep key subjects in frame—such as + * faces or detected objects (e.g., `fo-face`, `fo-person`, `fo-car`)— while + * resizing. + * + * - See [Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo). + * - [Object aware cropping](https://imagekit.io/docs/image-resize-and-crop#object-aware-cropping---fo-object-name) */ - startOffset?: number | string; + focus?: string; /** - * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to define a time window. Arithmetic expressions are supported. - * - * [Trim Videos - End Offset](https://imagekit.io/docs/trim-videos#end-offset---eo) + * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, + * `mp4`, or `auto`. You can also pass `orig` for images to return the original + * format. ImageKit automatically delivers images and videos in the optimal format + * based on device support unless overridden by the dashboard settings or the + * format parameter. See + * [Image format](https://imagekit.io/docs/image-optimization#format---f) and + * [Video format](https://imagekit.io/docs/video-optimization#format---f). */ - endOffset?: number | string; + format?: 'auto' | 'webp' | 'jpg' | 'jpeg' | 'png' | 'gif' | 'svg' | 'mp4' | 'webm' | 'avif' | 'orig'; /** - * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to indicate the length from the start offset. Arithmetic expressions are supported. - * - * [Trim Videos - Duration](https://imagekit.io/docs/trim-videos#duration---du) + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. See + * [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). */ - duration?: number | string; + gradient?: true | string; /** - * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, `480`, `720`, `1080`]. - * - * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) + * Enables a grayscale effect for images. See + * [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). */ - streamingResolutions?: StreamingResolution[]; + grayscale?: true; /** - * Enables a grayscale effect for images. - * - * [Effects and Enhancements - Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale) + * Specifies the height of the output. If a value between 0 and 1 is provided, it + * is treated as a percentage (e.g., `0.5` represents 50% of the original height). + * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). Height + * transformation – + * [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) */ - grayscale?: true; + height?: number | string; /** - * Upscales images beyond their original dimensions using AI. Not supported inside overlay. - * - * [AI Transformations - Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale) + * Specifies whether the output image (in JPEG or PNG) should be compressed + * losslessly. See + * [Lossless compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo). */ - aiUpscale?: true + lossless?: boolean; /** - * Performs AI-based retouching to improve faces or product shots. Not supported inside overlay. - * - * [AI Transformations - Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch) + * By default, ImageKit removes all metadata during automatic image compression. + * Set this to true to preserve metadata. See + * [Image metadata](https://imagekit.io/docs/image-optimization#image-metadata---md). */ - aiRetouch?: true + metadata?: boolean; /** - * Generates a variation of an image using AI. This produces a new image with slight variations from the original, - * such as changes in color, texture, and other visual elements, while preserving the structure and essence of the original image. Not supported inside overlay. - * - * [AI Transformations - Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar) + * Named transformation reference. See + * [Named transformations](https://imagekit.io/docs/transformations#named-transformations). */ - aiVariation?: true + named?: string; /** - * Adds an AI-based drop shadow around a foreground object on a transparent or removed background. - * Optionally, control the direction, elevation, and saturation of the light source (e.g., `az-45` to change light direction). - * Pass `true` for the default drop shadow, or provide a string for a custom drop shadow. - * Supported inside overlay. - * - * [AI Transformations - Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow) + * Specifies the opacity level of the output image. See + * [Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o). */ - aiDropShadow?: true | string + opacity?: number; /** - * Uses AI to change the background. Provide a text prompt or a base64-encoded prompt, - * e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. - * Not supported inside overlay. - * - * [AI Transformations - Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg) + * If set to true, serves the original file without applying any transformations. + * See + * [Deliver original file as-is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true). */ - aiChangeBackground?: string; + original?: boolean; /** - * Applies ImageKit’s in-house background removal. - * Supported inside overlay. - * - * [AI Transformations - Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove) + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). */ - aiRemoveBackground?: true + overlay?: Overlay; /** - * Uses third-party background removal. - * Note: It is recommended to use aiRemoveBackground, ImageKit’s in-house solution, which is more cost-effective. - * Supported inside overlay. - * - * [AI Transformations - External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg) + * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, + * AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the + * 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). See + * [Thumbnail extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). */ - aiRemoveBackgroundExternal?: true + page?: number | string; /** - * Automatically enhances the contrast of an image (contrast stretch). - * - * [Effects and Enhancements - Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast) + * Specifies whether the output JPEG image should be rendered progressively. + * Progressive loading begins with a low-quality, pixelated version of the full + * image, which gradually improves to provide a faster perceived load time. See + * [Progressive images](https://imagekit.io/docs/image-optimization#progressive-image---pr). */ - contrastStretch?: true + progressive?: boolean; /** - * Adds a shadow beneath solid objects in an image with a transparent background. - * For AI-based drop shadows, refer to aiDropShadow. - * Pass `true` for a default shadow, or provide a string for a custom shadow. - * - * [Effects and Enhancements - Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow) + * Specifies the quality of the output image for lossy formats such as JPEG, WebP, + * and AVIF. A higher quality value results in a larger file size with better + * quality, while a lower value produces a smaller file size with reduced quality. + * See [Quality](https://imagekit.io/docs/image-optimization#quality---q). */ - shadow?: true | string + quality?: number; /** - * Sharpens the input image, highlighting edges and finer details. - * Pass `true` for default sharpening, or provide a numeric value for custom sharpening. - * - * [Effects and Enhancements - Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen) + * Specifies the corner radius for rounded corners. + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). */ - sharpen?: true | number + radius?: number | 'max' | string; /** - * Applies Unsharp Masking (USM), an image sharpening technique. - * Pass `true` for a default unsharp mask, or provide a string for a custom unsharp mask. - * - * [Effects and Enhancements - Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm) + * Pass any transformation not directly supported by the SDK. This transformation + * string is appended to the URL as provided. */ - unsharpMask?: true | string; + raw?: string; /** - * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. - * - * [Effects and Enhancements - Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient) + * Specifies the rotation angle in degrees. Positive values rotate the image + * clockwise; you can also use, for example, `N40` for counterclockwise rotation or + * `auto` to use the orientation specified in the image's EXIF data. For videos, + * only the following values are supported: 0, 90, 180, 270, or 360. See + * [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). */ - gradient?: true | string; + rotation?: number | string; /** - * Specifies whether the output JPEG image should be rendered progressively. Progressive loading begins with a low-quality, - * pixelated version of the full image, which gradually improves to provide a faster perceived load time. - * - * [Image Optimization - Progressive Image](https://imagekit.io/docs/image-optimization#progressive-image---pr) + * Adds a shadow beneath solid objects in an image with a transparent background. + * For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default + * shadow, or provide a string for a custom shadow. See + * [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). */ - progressive?: boolean; + shadow?: true | string; /** - * Specifies whether the output image (in JPEG or PNG) should be compressed losslessly. - * - * [Image Optimization - Lossless Compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo) + * Sharpens the input image, highlighting edges and finer details. Pass `true` for + * default sharpening, or provide a numeric value for custom sharpening. See + * [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). */ - lossless?: boolean + sharpen?: true | number; /** - * Indicates whether the output image should retain the original color profile. - * - * [Image Optimization - Color Profile](https://imagekit.io/docs/image-optimization#color-profile---cp) + * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or + * `10.5`. Arithmetic expressions are also supported. See + * [Trim videos – Start offset](https://imagekit.io/docs/trim-videos#start-offset---so). */ - colorProfile?: boolean; + startOffset?: number | string; /** - * By default, ImageKit removes all metadata during automatic image compression. - * Set this to true to preserve metadata. - * - * [Image Optimization - Image Metadata](https://imagekit.io/docs/image-optimization#image-metadata---md) + * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, + * `480`, `720`, `1080`]. See + * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming). */ - metadata?: boolean; + streamingResolutions?: Array; /** - * Specifies the opacity level of the output image. - * - * [Effects and Enhancements - Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o) + * Useful for images with a solid or nearly solid background and a central object. + * This parameter trims the background, leaving only the central object in the + * output image. See + * [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). */ - opacity?: number; + trim?: true | number; /** - * Useful for images with a solid or nearly solid background and a central object. This parameter trims the background, - * leaving only the central object in the output image. - * - * [Effects and Enhancements - Trim Edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t) + * Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` for a + * default unsharp mask, or provide a string for a custom unsharp mask. See + * [Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). */ - trim?: true | number; + unsharpMask?: true | string; /** - * Accepts a numeric value that determines how much to zoom in or out of the cropped area. - * It should be used in conjunction with fo-face or fo-. - * - * [Image Resize and Crop - Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z) + * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See + * [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). */ - zoom?: number; + videoCodec?: 'h264' | 'vp9' | 'av1' | 'none'; /** - * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, AI). - * For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the 2nd and 3rd layers), - * or by name (e.g., `name-layer-4` for a PSD layer). - * - * [Vector and Animated Images - Thumbnail Extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files) + * Specifies the width of the output. If a value between 0 and 1 is provided, it is + * treated as a percentage (e.g., `0.4` represents 40% of the original width). You + * can also supply arithmetic expressions (e.g., `iw_div_2`). Width transformation + * – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) */ - page?: number | string; + width?: number | string; /** - * Pass any transformation not directly supported by the SDK. - * This transformation string is appended to the URL as provided. + * Focus using cropped image coordinates - X coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). */ - raw?: string; + x?: number | string; + + /** + * Focus using cropped image coordinates - X center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + xCenter?: number | string; + /** + * Focus using cropped image coordinates - Y coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + y?: number | string; /** - * Specifies an overlay to be applied on the parent image or video. - * ImageKit supports overlays including images, text, videos, subtitles, and solid colors. - * - * [Transformations - Overlay Using Layers](https://imagekit.io/docs/transformations#overlay-using-layers) + * Focus using cropped image coordinates - Y center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). */ - overlay?: Overlay; + yCenter?: number | string; + + /** + * Accepts a numeric value that determines how much to zoom in or out of the + * cropped area. It should be used in conjunction with fo-face or fo-. + * See [Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z). + */ + zoom?: number; } -export type Overlay = - | TextOverlay - | ImageOverlay - | VideoOverlay - | SubtitleOverlay - | SolidColorOverlay +/** + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + */ +export type Overlay = TextOverlay | ImageOverlay | VideoOverlay | SubtitleOverlay | SolidColorOverlay; export interface BaseOverlay { /** - * Specifies the overlay's position relative to the parent asset. - * Accepts a JSON object with `x` and `y` (or `focus`) properties. - * - * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) + * Controls how the layer blends with the base image or underlying content. Maps to + * `lm` in the URL. By default, layers completely cover the base image beneath + * them. Layer modes change this behavior: + * + * - `multiply`: Multiplies the pixel values of the layer with the base image. The + * result is always darker than the original images. This is ideal for applying + * shadows or color tints. + * - `displace`: Uses the layer as a displacement map to distort pixels in the base + * image. The red channel controls horizontal displacement, and the green channel + * controls vertical displacement. Requires `x` or `y` parameter to control + * displacement magnitude. + * - `cutout`: Acts as an inverse mask where opaque areas of the layer turn the + * base image transparent, while transparent areas leave the base image + * unchanged. This mode functions like a hole-punch, effectively cutting the + * shape of the layer out of the underlying image. + * - `cutter`: Acts as a shape mask where only the parts of the base image that + * fall inside the opaque area of the layer are preserved. This mode functions + * like a cookie-cutter, trimming the base image to match the specific dimensions + * and shape of the layer. See + * [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + */ + layerMode?: 'multiply' | 'cutter' | 'cutout' | 'displace'; + + /** + * Specifies the overlay's position relative to the parent asset. See + * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). */ position?: OverlayPosition; /** - * Specifies timing information for the overlay (only applicable if the base asset is a video). - * Accepts a JSON object with `start` (`lso`), `end` (`leo`), and `duration` (`ldu`) properties. - * - * [Transformations - Position of Layer](https://imagekit.io/docs/transformations#position-of-layer) + * Specifies timing information for the overlay (only applicable if the base asset + * is a video). See + * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). */ timing?: OverlayTiming; } export interface OverlayPosition { /** - * Specifies the x-coordinate of the top-left corner of the base asset where the overlay's top-left corner will be positioned. - * It also accepts arithmetic expressions such as `bw_mul_0.4` or `bw_sub_cw`. - * Maps to `lx` in the URL. - * - * Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + * Specifies the position of the overlay relative to the parent image or video. + * Maps to `lfo` in the URL. */ - x?: number | string; + focus?: + | 'center' + | 'top' + | 'left' + | 'bottom' + | 'right' + | 'top_left' + | 'top_right' + | 'bottom_left' + | 'bottom_right'; /** - * Specifies the y-coordinate of the top-left corner of the base asset where the overlay's top-left corner will be positioned. - * It also accepts arithmetic expressions such as `bh_mul_0.4` or `bh_sub_ch`. - * Maps to `ly` in the URL. - * - * Learn about [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations) + * Specifies the x-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). */ - y?: number | string; + x?: number | string; /** - * Specifies the position of the overlay relative to the parent image or video. - * Acceptable values: `center`, `top`, `left`, `bottom`, `right`, `top_left`, `top_right`, `bottom_left`, or `bottom_right`. - * Maps to `lfo` in the URL. + * Specifies the y-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). */ - focus?: `center` | `top` | `left` | `bottom` | `right` | `top_left` | `top_right` | `bottom_left` | `bottom_right`; + y?: number | string; } export interface OverlayTiming { /** - * Specifies the start time (in seconds) for when the overlay should appear on the base video. - * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. - * Applies only if the base asset is a video. - * - * Maps to `lso` in the URL. + * Specifies the duration (in seconds) during which the overlay should appear on + * the base video. Accepts a positive number up to two decimal places (e.g., `20` + * or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `ldu` in the URL. */ - start?: number | string; + duration?: number | string; /** - * Specifies the duration (in seconds) during which the overlay should appear on the base video. - * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. - * Applies only if the base asset is a video. - * - * Maps to `ldu` in the URL. + * Specifies the end time (in seconds) for when the overlay should disappear from + * the base video. If both end and duration are provided, duration is ignored. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and + * arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies only if + * the base asset is a video. Maps to `leo` in the URL. */ - duration?: number | string; + end?: number | string; /** - * Specifies the end time (in seconds) for when the overlay should disappear from the base video. - * If both end and duration are provided, duration is ignored. - * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. - * Applies only if the base asset is a video. - * - * Maps to `leo` in the URL. + * Specifies the start time (in seconds) for when the overlay should appear on the + * base video. Accepts a positive number up to two decimal places (e.g., `20` or + * `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `lso` in the URL. */ - end?: number | string; + start?: number | string; } export interface TextOverlay extends BaseOverlay { - type: "text"; - /** - * Specifies the text to be displayed in the overlay. The SDK automatically handles special characters and encoding. + * Specifies the text to be displayed in the overlay. The SDK automatically handles + * special characters and encoding. */ text: string; + type: 'text'; + /** - * Text can be included in the layer as either `i-{input}` (plain text) or `ie-{base64_encoded_input}` (base64). - * By default, the SDK selects the appropriate format based on the input text. - * To always use base64 (`ie-{base64}`), set this parameter to `base64`. - * To always use plain text (`i-{input}`), set it to `plain`. - * - * Regardless of the encoding method, the input text is always percent-encoded to ensure it is URL-safe. + * Text can be included in the layer as either `i-{input}` (plain text) or + * `ie-{base64_encoded_input}` (base64). By default, the SDK selects the + * appropriate format based on the input text. To always use base64 + * (`ie-{base64}`), set this parameter to `base64`. To always use plain text + * (`i-{input}`), set it to `plain`. + * + * Regardless of the encoding method, the input text is always percent-encoded to + * ensure it is URL-safe. */ - - encoding?: "auto" | "plain" | "base64"; + encoding?: 'auto' | 'plain' | 'base64'; /** - * Control styling of the text overlay. + * Control styling of the text overlay. See + * [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). */ - transformation?: TextOverlayTransformation[]; + transformation?: Array; } export interface ImageOverlay extends BaseOverlay { @@ -533,206 +613,297 @@ export interface ImageOverlay extends BaseOverlay { } export interface VideoOverlay extends BaseOverlay { - type: "video"; /** * Specifies the relative path to the video used as an overlay. */ input: string; + type: 'video'; + /** - * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. - * By default, the SDK determines the appropriate format automatically. - * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. - * To always use plain text (`i-{input}`), set it to `plain`. - * + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + * * Regardless of the encoding method: + * * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. + * - Remaining slashes within the path are replaced with `@@` when using plain + * text. */ - encoding?: "auto" | "plain" | "base64"; + encoding?: 'auto' | 'plain' | 'base64'; /** - * Array of transformation to be applied to the overlay video. Except `streamingResolutions`, all other video transformations are supported. - * - * [Video Transformations](https://imagekit.io/docs/video-transformation) + * Array of transformation to be applied to the overlay video. Except + * `streamingResolutions`, all other video transformations are supported. See + * [Video transformations](https://imagekit.io/docs/video-transformation). */ - transformation?: Transformation[]; + transformation?: Array; } export interface SubtitleOverlay extends BaseOverlay { - type: "subtitle"; /** * Specifies the relative path to the subtitle file used as an overlay. */ input: string; + type: 'subtitle'; + /** - * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. - * By default, the SDK determines the appropriate format automatically. - * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. - * To always use plain text (`i-{input}`), set it to `plain`. - * + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + * * Regardless of the encoding method: + * * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. + * - Remaining slashes within the path are replaced with `@@` when using plain + * text. */ - encoding?: "auto" | "plain" | "base64"; + encoding?: 'auto' | 'plain' | 'base64'; /** - * Control styling of the subtitle. - * - * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + * Control styling of the subtitle. See + * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). */ - transformation?: SubtitleOverlayTransformation[]; + transformation?: Array; } export interface SolidColorOverlay extends BaseOverlay { - type: "solidColor"; /** - * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name (e.g., `red`). - * If an 8-character value is provided, the last two characters represent the opacity level (from `00` for 0.00 to `99` for 0.99). + * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA + * code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character value + * is provided, the last two characters represent the opacity level (from `00` for + * 0.00 to `99` for 0.99). */ color: string; + type: 'solidColor'; + /** - * Control width and height of the solid color overlay. Supported transformations depend on the base/parent asset. - * - * [Image](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) | [Video](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay) + * Control width and height of the solid color overlay. Supported transformations + * depend on the base/parent asset. See overlays on + * [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) + * and + * [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). */ - transformation?: SolidColorOverlayTransformation[]; + transformation?: Array; } -export type TextOverlayTransformation = { +export interface TextOverlayTransformation { /** - * Specifies the maximum width (in pixels) of the overlaid text. The text wraps automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are supported. Useful when used in conjunction with the `background`. + * Specifies the transparency level of the text overlay. Accepts integers from `1` + * to `9`. */ - width?: number | string; + alpha?: number; /** - * Specifies the font size of the overlaid text. Accepts a numeric value or an arithmetic expression. + * Specifies the background color of the text overlay. Accepts an RGB hex code, an + * RGBA code, or a color name. */ - fontSize?: number | string; + background?: string; /** - * Specifies the font family of the overlaid text. Choose from the [supported fonts list](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) or use a [custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). + * Flip/mirror the text horizontally, vertically, or in both directions. Acceptable + * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or + * `v_h`. */ - fontFamily?: string; + flip?: 'h' | 'v' | 'h_v' | 'v_h'; /** - * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., + * `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. */ fontColor?: string; /** - * Specifies the inner alignment of the text when width is more than the text length. - * Supported values: `left`, `right`, and `center` (default). + * Specifies the font family of the overlaid text. Choose from the supported fonts + * list or use a custom font. See + * [Supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + * and + * [Custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). */ - innerAlignment?: "left" | "right" | "center"; + fontFamily?: string; /** - * Specifies the padding around the overlaid text. - * Can be provided as a single positive integer or multiple values separated by underscores (following CSS shorthand order). - * Arithmetic expressions are also accepted. + * Specifies the font size of the overlaid text. Accepts a numeric value or an + * arithmetic expression. */ - padding?: number | string; + fontSize?: number | string; /** - * Specifies the transparency level of the text overlay. Accepts integers from `1` to `9`. + * Specifies the inner alignment of the text when width is more than the text + * length. */ - alpha?: number; + innerAlignment?: 'left' | 'right' | 'center'; /** - * Specifies the typography style of the text. - * Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. + * Specifies the line height for multi-line text overlays. It will come into effect + * only if the text wraps over multiple lines. Accepts either an integer value or + * an arithmetic expression. */ - typography?: "b" | "i" | "b_i"; + lineHeight?: number | string; /** - * Specifies the background color of the text overlay. - * Accepts an RGB hex code, an RGBA code, or a color name. + * Specifies the padding around the overlaid text. Can be provided as a single + * positive integer or multiple values separated by underscores (following CSS + * shorthand order). Arithmetic expressions are also accepted. */ - background?: string; + padding?: number | string; /** - * Specifies the corner radius of the text overlay. - * Set to `max` to achieve a circular or oval shape. + * Specifies the corner radius: + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). */ - radius?: number | "max"; + radius?: number | 'max' | string; /** - * Specifies the rotation angle of the text overlay. - * Accepts a numeric value for clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. + * Specifies the rotation angle of the text overlay. Accepts a numeric value for + * clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. */ rotation?: number | string; /** - * Flip/mirror the text horizontally, vertically, or in both directions. - * Acceptable values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or `v_h`. + * Specifies the typography style of the text. Supported values: + * + * - Single styles: `b` (bold), `i` (italic), `strikethrough`. + * - Combinations: Any combination separated by underscores, e.g., `b_i`, + * `b_i_strikethrough`. */ - flip?: "h" | "v" | "h_v" | "v_h"; + typography?: string; /** - * Specifies the line height for multi-line text overlays. It will come into effect only if the text wraps over multiple lines. - * Accepts either an integer value or an arithmetic expression. + * Specifies the maximum width (in pixels) of the overlaid text. The text wraps + * automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are + * supported. Useful when used in conjunction with the `background`. Learn about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). */ - lineHeight?: number | string; + width?: number | string; } -export type SubtitleOverlayTransformation = { + +/** + * Subtitle styling options. + * [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + * from the docs. + */ +export interface SubtitleOverlayTransformation { /** - * Specifies the subtitle background color using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). + * Specifies the subtitle background color using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ background?: string; + /** - * Sets the font size of subtitle text. + * Sets the font color of the subtitle text using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ - fontSize?: number | string; + color?: string; + /** - * Sets the font family of subtitle text. - * Refer to the [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) in the ImageKit transformations guide. + * Sets the font family of subtitle text. Refer to the + * [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + * in the ImageKit transformations guide. */ fontFamily?: string; + /** - * Sets the font color of the subtitle text using a standard color name, an RGB color code (e.g., `FF0000`), or an RGBA color code (e.g., `FFAABB50`). + * Sets the font outline of the subtitle text. Requires the outline width (an + * integer) and the outline color (as an RGB color code, RGBA color code, or + * standard web color name) separated by an underscore. Example: `fol-2_blue` + * (outline width of 2px and outline color blue), `fol-2_A1CCDD` (outline width of + * 2px and outline color `#A1CCDD`) and `fol-2_A1CCDD50` (outline width of 2px and + * outline color `#A1CCDD` at 50% opacity). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ - color?: string; + fontOutline?: string; + /** - * Sets the typography style of the subtitle text. - * Supported values: `b` for bold, `i` for italics, and `b_i` for bold with italics. + * Sets the font shadow for the subtitle text. Requires the shadow color (as an RGB + * color code, RGBA color code, or standard web color name) and shadow indent (an + * integer) separated by an underscore. Example: `fsh-blue_2` (shadow color blue, + * indent of 2px), `fsh-A1CCDD_3` (shadow color `#A1CCDD`, indent of 3px), + * `fsh-A1CCDD50_3` (shadow color `#A1CCDD` at 50% opacity, indent of 3px). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ - typography?: "b" | "i" | "b_i"; + fontShadow?: string; + /** - * Sets the font outline of the subtitle text. - * Requires the outline width (an integer) and the outline color (as an RGB color code, RGBA color code, or standard web color name) separated by an underscore. - * Examples: `2_blue`, `2_A1CCDD`, or `2_A1CCDD50`. + * Sets the font size of subtitle text. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ - fontOutline?: string; + fontSize?: number; + /** - * Sets the font shadow for the subtitle text. - * Requires the shadow color (as an RGB color code, RGBA color code, or standard web color name) and the shadow indent (an integer) separated by an underscore. - * Examples: `blue_2`, `A1CCDD_3`, or `A1CCDD50_3`. + * Sets the typography style of the subtitle text. Supports values are `b` for + * bold, `i` for italics, and `b_i` for bold with italics. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) */ - fontShadow?: string; + typography?: 'b' | 'i' | 'b_i'; } -export type SolidColorOverlayTransformation = Pick & { +export interface SolidColorOverlayTransformation { /** - * Specifies the transparency level of the overlaid solid color layer. Supports integers from `1` to `9`. + * Specifies the transparency level of the overlaid solid color layer. Supports + * integers from `1` to `9`. */ alpha?: number; /** - * Specifies the background color of the solid color overlay. - * Accepts an RGB hex code, an RGBA code, or a color name. + * Specifies the background color of the solid color overlay. Accepts an RGB hex + * code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. */ background?: string; /** - * Only works if base asset is an image. - * - * Creates a linear gradient with two colors. Pass `true` for a default gradient, or provide a string for a custom gradient. - * - * [Effects and Enhancements - Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient) + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. Only works if the base asset is an + * image. See + * [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). */ - gradient?: Transformation["gradient"] + gradient?: true | string; + + /** + * Controls the height of the solid color overlay. Accepts a numeric value or an + * arithmetic expression. Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + height?: number | string; + + /** + * Specifies the corner radius of the solid color overlay. + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max' | string; + + /** + * Controls the width of the solid color overlay. Accepts a numeric value or an + * arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + width?: number | string; } diff --git a/src/url.ts b/src/url.ts index 836bb8e..62a12c2 100644 --- a/src/url.ts +++ b/src/url.ts @@ -124,7 +124,7 @@ function processText(str: string, enccoding: TextOverlay["encoding"]): string { function processOverlay(overlay: Transformation["overlay"]): string | undefined { const entries = []; - const { type, position = {}, timing = {}, transformation = [] } = overlay || {}; + const { type, layerMode, position = {}, timing = {}, transformation = [] } = overlay || {}; if (!type) { return; @@ -192,6 +192,11 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined break; } + // Add layer mode if specified + if (layerMode) { + entries.push(`lm-${layerMode}`); + } + const { x, y, focus } = position; if (x) { entries.push(`lx-${x}`); diff --git a/test/url-generation/basic.js b/test/url-generation/basic.js index 739958c..da928ee 100644 --- a/test/url-generation/basic.js +++ b/test/url-generation/basic.js @@ -1267,6 +1267,7 @@ describe("URL generation", function () { aiVariation: true, aiDropShadow: true, aiChangeBackground: "prompt-car", + aiEdit: 'prompt-make it vintage', aiRemoveBackground: true, contrastStretch: true, shadow: "bl-15_st-40_x-10_y-N5", @@ -1275,13 +1276,16 @@ describe("URL generation", function () { gradient: "from-red_to-white", original: true, page: "2_4", - raw: "h-200,w-300,l-image,i-logo.png,l-end" + raw: "h-200,w-300,l-image,i-logo.png,l-end", + // New transformation parameters + colorReplace: 'FF0000_50_00FF00', + distort: 'p-50_50_150_50_150_150_50_150', } ] }); expect(url).equal( - `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end` + `https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-edit-prompt-make it vintage,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end,cr-FF0000_50_00FF00,e-distort-p-50_50_150_50_150_150_50_150` ); }); }); diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index 0ade645..b1edd4c 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -509,4 +509,75 @@ describe("Overlay encoding test cases", function () { }); expect(url).equal(`https://ik.imagekit.io/demo/sample.jpg?tr=l-text,i-Minimal%20Text,l-end`); }); + + // Layer Mode Tests + describe("Layer Mode Tests", function () { + it('should generate correct URL with multiply layer mode', function () { + const url = buildSrc({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", + input: "overlay-image.jpg", + layerMode: "multiply" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay-image.jpg,lm-multiply,l-end/base-image.jpg`); + }); + + it('should generate correct URL with cutter layer mode', function () { + const url = buildSrc({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", + input: "overlay-image.jpg", + layerMode: "cutter" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay-image.jpg,lm-cutter,l-end/base-image.jpg`); + }); + + it('should generate correct URL with cutout layer mode', function () { + const url = buildSrc({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", + input: "overlay-image.jpg", + layerMode: "cutout" + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay-image.jpg,lm-cutout,l-end/base-image.jpg`); + }); + + it('should generate correct URL with displace layer mode', function () { + const url = buildSrc({ + transformationPosition: "path", + urlEndpoint: "https://ik.imagekit.io/test_url_endpoint", + src: "/base-image.jpg", + transformation: [{ + overlay: { + type: "image", + input: "overlay-image.jpg", + layerMode: "displace", + position: { + x: 10, + y: 10 + } + } + }] + }); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay-image.jpg,lm-displace,lx-10,ly-10,l-end/base-image.jpg`); + }); + }); }); From e6fedf93fa1e9ffcf47830ebf02fc80f0458443c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 16 Jan 2026 16:43:05 +0530 Subject: [PATCH 162/166] Pick types from generated Node.js SDK --- src/index.ts | 16 +- src/interfaces/SrcOptions.ts | 35 - src/interfaces/Transformation.ts | 909 ---------------- src/interfaces/UploadOptions.ts | 340 ++++-- src/interfaces/UploadResponse.ts | 587 +++++++--- src/interfaces/index.ts | 5 +- src/interfaces/shared.ts | 1725 ++++++++++++++++++++++++++++++ src/responsive.ts | 59 +- src/url.ts | 3 +- 9 files changed, 2432 insertions(+), 1247 deletions(-) delete mode 100644 src/interfaces/SrcOptions.ts delete mode 100644 src/interfaces/Transformation.ts create mode 100644 src/interfaces/shared.ts diff --git a/src/index.ts b/src/index.ts index bf68707..dbbfb11 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,12 @@ -import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import type { GetImageAttributesOptions, ResponsiveImageAttributes, SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; +import type { } from "./responsive"; +import { getResponsiveImageAttributes } from "./responsive"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; -import { getResponsiveImageAttributes } from "./responsive"; -import type { GetImageAttributesOptions, ResponsiveImageAttributes } from "./responsive"; -export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; +export { buildSrc, buildTransformationString, getResponsiveImageAttributes, ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload }; export type { - Transformation, - SrcOptions, - UploadOptions, - UploadResponse, - GetImageAttributesOptions, ResponsiveImageAttributes + GetImageAttributesOptions, ResponsiveImageAttributes, SrcOptions, Transformation, UploadOptions, + UploadResponse }; + diff --git a/src/interfaces/SrcOptions.ts b/src/interfaces/SrcOptions.ts deleted file mode 100644 index 4b0187e..0000000 --- a/src/interfaces/SrcOptions.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Transformation } from "./Transformation"; -import { TransformationPosition } from "."; - -export interface SrcOptions { - /** - * Accepts a relative or absolute path of the resource. If a relative path is provided, it is appended to the `urlEndpoint`. - * If an absolute path is provided, `urlEndpoint` is ignored. - */ - src: string; - - /** - * Get your urlEndpoint from the [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). - */ - urlEndpoint: string; - - /** - * An array of objects specifying the transformations to be applied in the URL. If more than one transformation is specified, they are applied in the order they are specified as chained transformations. - * - * {@link https://imagekit.io/docs/transformations#chained-transformations} - */ - transformation?: Array; - - /** - * These are additional query parameters that you want to add to the final URL. - * They can be any query parameters and not necessarily related to ImageKit. - * This is especially useful if you want to add a versioning parameter to your URLs. - */ - queryParameters?: { [key: string]: string | number }; - - /** - * By default, the transformation string is added as a query parameter in the URL, e.g., `?tr=w-100,h-100`. - * If you want to add the transformation string in the path of the URL, set this to `path`. - */ - transformationPosition?: TransformationPosition; -} diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts deleted file mode 100644 index f517855..0000000 --- a/src/interfaces/Transformation.ts +++ /dev/null @@ -1,909 +0,0 @@ -/** - * By default, the transformation string is added as a query parameter in the URL, - * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the - * path of the URL, set this to `path`. Learn more in the - * [Transformations guide](https://imagekit.io/docs/transformations). - */ -export type TransformationPosition = "path" | "query"; - -export type StreamingResolution = "240" | "360" | "480" | "720" | "1080" | "1440" | "2160"; - -/** - * The SDK provides easy-to-use names for transformations. These names are - * converted to the corresponding transformation string before being added to the - * URL. SDKs are updated regularly to support new transformations. If you want to - * use a transformation that is not supported by the SDK, You can use the `raw` - * parameter to pass the transformation string directly. See the - * [Transformations documentation](https://imagekit.io/docs/transformations). - */ -export interface Transformation { - /** - * Uses AI to change the background. Provide a text prompt or a base64-encoded - * prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. - * Not supported inside overlay. See - * [AI Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg). - */ - aiChangeBackground?: string; - - /** - * Adds an AI-based drop shadow around a foreground object on a transparent or - * removed background. Optionally, control the direction, elevation, and saturation - * of the light source (e.g., `az-45` to change light direction). Pass `true` for - * the default drop shadow, or provide a string for a custom drop shadow. Supported - * inside overlay. See - * [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). - */ - aiDropShadow?: true | string; - - /** - * Uses AI to edit images based on a text prompt. Provide a text prompt or a - * base64-encoded prompt, e.g., `prompt-snow road` or - * `prompte-[urlencoded_base64_encoded_text]`. Not supported inside overlay. - * See [AI Edit](https://imagekit.io/docs/ai-transformations#edit-image-e-edit). - */ - aiEdit?: string; - - /** - * Applies ImageKit's in-house background removal. Supported inside overlay. See - * [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). - */ - aiRemoveBackground?: true; - - /** - * Uses third-party background removal. Note: It is recommended to use - * aiRemoveBackground, ImageKit's in-house solution, which is more cost-effective. - * Supported inside overlay. See - * [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). - */ - aiRemoveBackgroundExternal?: true; - - /** - * Performs AI-based retouching to improve faces or product shots. Not supported - * inside overlay. See - * [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). - */ - aiRetouch?: true; - - /** - * Upscales images beyond their original dimensions using AI. Not supported inside - * overlay. See - * [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). - */ - aiUpscale?: true; - - /** - * Generates a variation of an image using AI. This produces a new image with - * slight variations from the original, such as changes in color, texture, and - * other visual elements, while preserving the structure and essence of the - * original image. Not supported inside overlay. See - * [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). - */ - aiVariation?: true; - - /** - * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with - * either width or height (but not both). For example: aspectRatio = `4:3`, `4_3`, - * or an expression like `iar_div_2`. See - * [Image resize and crop – Aspect ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). - */ - aspectRatio?: number | string; - - /** - * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See - * [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). - */ - audioCodec?: 'aac' | 'opus' | 'none'; - - /** - * Specifies the background to be used in conjunction with certain cropping - * strategies when resizing an image. - * - * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. See - * [Solid color background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background). - * - Dominant color: `dominant` extracts the dominant color from the image. See - * [Dominant color background](https://imagekit.io/docs/effects-and-enhancements#dominant-color-background). - * - Gradient: `gradient_dominant` or `gradient_dominant_2` creates a gradient - * using the dominant colors. Optionally specify palette size (2 or 4), e.g., - * `gradient_dominant_4`. See - * [Gradient background](https://imagekit.io/docs/effects-and-enhancements#gradient-background). - * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. See - * [Blurred background](https://imagekit.io/docs/effects-and-enhancements#blurred-background). - * - Expand the image boundaries using generative fill: `genfill`. Not supported - * inside overlay. Optionally, control the background scene by passing a text - * prompt: `genfill[:-prompt-${text}]` or - * `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. See - * [Generative fill background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill). - */ - background?: string; - - /** - * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, - * or an expression like `bl-10`. See - * [Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl). - */ - blur?: number; - - /** - * Adds a border to the output media. Accepts a string in the format - * `_` (e.g., `5_FFF000` for a 5px yellow border), or an - * expression like `ih_div_20_FF00FF`. See - * [Border](https://imagekit.io/docs/effects-and-enhancements#border---b). - */ - border?: string; - - /** - * Indicates whether the output image should retain the original color profile. See - * [Color profile](https://imagekit.io/docs/image-optimization#color-profile---cp). - */ - colorProfile?: boolean; - - /** - * Replaces colors in the image. Supports three formats: - * - * - `toColor` - Replace dominant color with the specified color. - * - `toColor_tolerance` - Replace dominant color with specified tolerance (0-100). - * - `toColor_tolerance_fromColor` - Replace a specific color with another within - * tolerance range. Colors can be hex codes (e.g., `FF0022`) or names (e.g., - * `red`, `blue`). See - * [Color replacement](https://imagekit.io/docs/effects-and-enhancements#color-replace---cr). - */ - colorReplace?: string; - - /** - * Automatically enhances the contrast of an image (contrast stretch). See - * [Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). - */ - contrastStretch?: true; - - /** - * Crop modes for image resizing. See - * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). - */ - crop?: 'force' | 'at_max' | 'at_max_enlarge' | 'at_least' | 'maintain_ratio'; - - /** - * Additional crop modes for image resizing. See - * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). - */ - cropMode?: 'pad_resize' | 'extract' | 'pad_extract'; - - /** - * Specifies a fallback image if the resource is not found, e.g., a URL or file - * path. See - * [Default image](https://imagekit.io/docs/image-transformation#default-image---di). - */ - defaultImage?: string; - - /** - * Distorts the shape of an image. Supports two modes: - * - * - Perspective distortion: `p-x1_y1_x2_y2_x3_y3_x4_y4` changes the position of - * the four corners starting clockwise from top-left. - * - Arc distortion: `a-degrees` curves the image upwards (positive values) or - * downwards (negative values). See - * [Distort effect](https://imagekit.io/docs/effects-and-enhancements#distort---e-distort). - */ - distort?: string; - - /** - * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio - * (DPR) calculation. Also accepts arithmetic expressions. - * - * - Learn about - * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - * - See [DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr). - */ - dpr?: number; - - /** - * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to indicate the length from the start offset. - * Arithmetic expressions are supported. See - * [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). - */ - duration?: number | string; - - /** - * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. - * Typically used with startOffset to define a time window. Arithmetic expressions - * are supported. See - * [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). - */ - endOffset?: number | string; - - /** - * Flips or mirrors an image either horizontally, vertically, or both. Acceptable - * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or - * `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). - */ - flip?: 'h' | 'v' | 'h_v' | 'v_h'; - - /** - * Refines padding and cropping behavior for pad resize, maintain ratio, and - * extract crop modes. Supports manual positions and coordinate-based focus. With - * AI-based cropping, you can automatically keep key subjects in frame—such as - * faces or detected objects (e.g., `fo-face`, `fo-person`, `fo-car`)— while - * resizing. - * - * - See [Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo). - * - [Object aware cropping](https://imagekit.io/docs/image-resize-and-crop#object-aware-cropping---fo-object-name) - */ - focus?: string; - - /** - * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, - * `mp4`, or `auto`. You can also pass `orig` for images to return the original - * format. ImageKit automatically delivers images and videos in the optimal format - * based on device support unless overridden by the dashboard settings or the - * format parameter. See - * [Image format](https://imagekit.io/docs/image-optimization#format---f) and - * [Video format](https://imagekit.io/docs/video-optimization#format---f). - */ - format?: 'auto' | 'webp' | 'jpg' | 'jpeg' | 'png' | 'gif' | 'svg' | 'mp4' | 'webm' | 'avif' | 'orig'; - - /** - * Creates a linear gradient with two colors. Pass `true` for a default gradient, - * or provide a string for a custom gradient. See - * [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). - */ - gradient?: true | string; - - /** - * Enables a grayscale effect for images. See - * [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). - */ - grayscale?: true; - - /** - * Specifies the height of the output. If a value between 0 and 1 is provided, it - * is treated as a percentage (e.g., `0.5` represents 50% of the original height). - * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). Height - * transformation – - * [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) · - * [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) - */ - height?: number | string; - - /** - * Specifies whether the output image (in JPEG or PNG) should be compressed - * losslessly. See - * [Lossless compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo). - */ - lossless?: boolean; - - /** - * By default, ImageKit removes all metadata during automatic image compression. - * Set this to true to preserve metadata. See - * [Image metadata](https://imagekit.io/docs/image-optimization#image-metadata---md). - */ - metadata?: boolean; - - /** - * Named transformation reference. See - * [Named transformations](https://imagekit.io/docs/transformations#named-transformations). - */ - named?: string; - - /** - * Specifies the opacity level of the output image. See - * [Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o). - */ - opacity?: number; - - /** - * If set to true, serves the original file without applying any transformations. - * See - * [Deliver original file as-is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true). - */ - original?: boolean; - - /** - * Specifies an overlay to be applied on the parent image or video. ImageKit - * supports overlays including images, text, videos, subtitles, and solid colors. - * See - * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). - */ - overlay?: Overlay; - - /** - * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, - * AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the - * 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). See - * [Thumbnail extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). - */ - page?: number | string; - - /** - * Specifies whether the output JPEG image should be rendered progressively. - * Progressive loading begins with a low-quality, pixelated version of the full - * image, which gradually improves to provide a faster perceived load time. See - * [Progressive images](https://imagekit.io/docs/image-optimization#progressive-image---pr). - */ - progressive?: boolean; - - /** - * Specifies the quality of the output image for lossy formats such as JPEG, WebP, - * and AVIF. A higher quality value results in a larger file size with better - * quality, while a lower value produces a smaller file size with reduced quality. - * See [Quality](https://imagekit.io/docs/image-optimization#quality---q). - */ - quality?: number; - - /** - * Specifies the corner radius for rounded corners. - * - * - Single value (positive integer): Applied to all corners (e.g., `20`). - * - `max`: Creates a circular or oval shape. - * - Per-corner array: Provide four underscore-separated values representing - * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., - * `10_20_30_40`). See - * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). - */ - radius?: number | 'max' | string; - - /** - * Pass any transformation not directly supported by the SDK. This transformation - * string is appended to the URL as provided. - */ - raw?: string; - - /** - * Specifies the rotation angle in degrees. Positive values rotate the image - * clockwise; you can also use, for example, `N40` for counterclockwise rotation or - * `auto` to use the orientation specified in the image's EXIF data. For videos, - * only the following values are supported: 0, 90, 180, 270, or 360. See - * [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). - */ - rotation?: number | string; - - /** - * Adds a shadow beneath solid objects in an image with a transparent background. - * For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default - * shadow, or provide a string for a custom shadow. See - * [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). - */ - shadow?: true | string; - - /** - * Sharpens the input image, highlighting edges and finer details. Pass `true` for - * default sharpening, or provide a numeric value for custom sharpening. See - * [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). - */ - sharpen?: true | number; - - /** - * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or - * `10.5`. Arithmetic expressions are also supported. See - * [Trim videos – Start offset](https://imagekit.io/docs/trim-videos#start-offset---so). - */ - startOffset?: number | string; - - /** - * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, - * `480`, `720`, `1080`]. See - * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming). - */ - streamingResolutions?: Array; - - /** - * Useful for images with a solid or nearly solid background and a central object. - * This parameter trims the background, leaving only the central object in the - * output image. See - * [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). - */ - trim?: true | number; - - /** - * Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` for a - * default unsharp mask, or provide a string for a custom unsharp mask. See - * [Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). - */ - unsharpMask?: true | string; - - /** - * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See - * [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). - */ - videoCodec?: 'h264' | 'vp9' | 'av1' | 'none'; - - /** - * Specifies the width of the output. If a value between 0 and 1 is provided, it is - * treated as a percentage (e.g., `0.4` represents 40% of the original width). You - * can also supply arithmetic expressions (e.g., `iw_div_2`). Width transformation - * – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) · - * [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) - */ - width?: number | string; - - /** - * Focus using cropped image coordinates - X coordinate. See - * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). - */ - x?: number | string; - - /** - * Focus using cropped image coordinates - X center coordinate. See - * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). - */ - xCenter?: number | string; - - /** - * Focus using cropped image coordinates - Y coordinate. See - * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). - */ - y?: number | string; - - /** - * Focus using cropped image coordinates - Y center coordinate. See - * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). - */ - yCenter?: number | string; - - /** - * Accepts a numeric value that determines how much to zoom in or out of the - * cropped area. It should be used in conjunction with fo-face or fo-. - * See [Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z). - */ - zoom?: number; -} - -/** - * Specifies an overlay to be applied on the parent image or video. ImageKit - * supports overlays including images, text, videos, subtitles, and solid colors. - * See - * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). - */ -export type Overlay = TextOverlay | ImageOverlay | VideoOverlay | SubtitleOverlay | SolidColorOverlay; - -export interface BaseOverlay { - /** - * Controls how the layer blends with the base image or underlying content. Maps to - * `lm` in the URL. By default, layers completely cover the base image beneath - * them. Layer modes change this behavior: - * - * - `multiply`: Multiplies the pixel values of the layer with the base image. The - * result is always darker than the original images. This is ideal for applying - * shadows or color tints. - * - `displace`: Uses the layer as a displacement map to distort pixels in the base - * image. The red channel controls horizontal displacement, and the green channel - * controls vertical displacement. Requires `x` or `y` parameter to control - * displacement magnitude. - * - `cutout`: Acts as an inverse mask where opaque areas of the layer turn the - * base image transparent, while transparent areas leave the base image - * unchanged. This mode functions like a hole-punch, effectively cutting the - * shape of the layer out of the underlying image. - * - `cutter`: Acts as a shape mask where only the parts of the base image that - * fall inside the opaque area of the layer are preserved. This mode functions - * like a cookie-cutter, trimming the base image to match the specific dimensions - * and shape of the layer. See - * [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). - */ - layerMode?: 'multiply' | 'cutter' | 'cutout' | 'displace'; - - /** - * Specifies the overlay's position relative to the parent asset. See - * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). - */ - position?: OverlayPosition; - - /** - * Specifies timing information for the overlay (only applicable if the base asset - * is a video). See - * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). - */ - timing?: OverlayTiming; -} - -export interface OverlayPosition { - /** - * Specifies the position of the overlay relative to the parent image or video. - * Maps to `lfo` in the URL. - */ - focus?: - | 'center' - | 'top' - | 'left' - | 'bottom' - | 'right' - | 'top_left' - | 'top_right' - | 'bottom_left' - | 'bottom_right'; - - /** - * Specifies the x-coordinate of the top-left corner of the base asset where the - * overlay's top-left corner will be positioned. It also accepts arithmetic - * expressions such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. Learn - * about - * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - */ - x?: number | string; - - /** - * Specifies the y-coordinate of the top-left corner of the base asset where the - * overlay's top-left corner will be positioned. It also accepts arithmetic - * expressions such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. Learn - * about - * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - */ - y?: number | string; -} - -export interface OverlayTiming { - /** - * Specifies the duration (in seconds) during which the overlay should appear on - * the base video. Accepts a positive number up to two decimal places (e.g., `20` - * or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. - * Applies only if the base asset is a video. Maps to `ldu` in the URL. - */ - duration?: number | string; - - /** - * Specifies the end time (in seconds) for when the overlay should disappear from - * the base video. If both end and duration are provided, duration is ignored. - * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and - * arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies only if - * the base asset is a video. Maps to `leo` in the URL. - */ - end?: number | string; - - /** - * Specifies the start time (in seconds) for when the overlay should appear on the - * base video. Accepts a positive number up to two decimal places (e.g., `20` or - * `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. - * Applies only if the base asset is a video. Maps to `lso` in the URL. - */ - start?: number | string; -} - -export interface TextOverlay extends BaseOverlay { - /** - * Specifies the text to be displayed in the overlay. The SDK automatically handles - * special characters and encoding. - */ - text: string; - - type: 'text'; - - /** - * Text can be included in the layer as either `i-{input}` (plain text) or - * `ie-{base64_encoded_input}` (base64). By default, the SDK selects the - * appropriate format based on the input text. To always use base64 - * (`ie-{base64}`), set this parameter to `base64`. To always use plain text - * (`i-{input}`), set it to `plain`. - * - * Regardless of the encoding method, the input text is always percent-encoded to - * ensure it is URL-safe. - */ - encoding?: 'auto' | 'plain' | 'base64'; - - /** - * Control styling of the text overlay. See - * [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). - */ - transformation?: Array; -} - -export interface ImageOverlay extends BaseOverlay { - type: "image"; - - /** - * Specifies the relative path to the image used as an overlay. - */ - input: string; - - /** - * The input path can be included in the layer as either `i-{input}` or `ie-{base64_encoded_input}`. - * By default, the SDK determines the appropriate format automatically. - * To always use base64 encoding (`ie-{base64}`), set this parameter to `base64`. - * To always use plain text (`i-{input}`), set it to `plain`. - * - * Regardless of the encoding method: - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain text. - */ - encoding?: "auto" | "plain" | "base64"; - - /** - * Array of transformations to be applied to the overlay image. Supported transformations depends on the base/parent asset. - * - * [Image](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) | [Video](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay) - */ - transformation?: Transformation[]; -} - -export interface VideoOverlay extends BaseOverlay { - /** - * Specifies the relative path to the video used as an overlay. - */ - input: string; - - type: 'video'; - - /** - * The input path can be included in the layer as either `i-{input}` or - * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate - * format automatically. To always use base64 encoding (`ie-{base64}`), set this - * parameter to `base64`. To always use plain text (`i-{input}`), set it to - * `plain`. - * - * Regardless of the encoding method: - * - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain - * text. - */ - encoding?: 'auto' | 'plain' | 'base64'; - - /** - * Array of transformation to be applied to the overlay video. Except - * `streamingResolutions`, all other video transformations are supported. See - * [Video transformations](https://imagekit.io/docs/video-transformation). - */ - transformation?: Array; -} - -export interface SubtitleOverlay extends BaseOverlay { - /** - * Specifies the relative path to the subtitle file used as an overlay. - */ - input: string; - - type: 'subtitle'; - - /** - * The input path can be included in the layer as either `i-{input}` or - * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate - * format automatically. To always use base64 encoding (`ie-{base64}`), set this - * parameter to `base64`. To always use plain text (`i-{input}`), set it to - * `plain`. - * - * Regardless of the encoding method: - * - * - Leading and trailing slashes are removed. - * - Remaining slashes within the path are replaced with `@@` when using plain - * text. - */ - encoding?: 'auto' | 'plain' | 'base64'; - - /** - * Control styling of the subtitle. See - * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). - */ - transformation?: Array; -} - -export interface SolidColorOverlay extends BaseOverlay { - /** - * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA - * code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character value - * is provided, the last two characters represent the opacity level (from `00` for - * 0.00 to `99` for 0.99). - */ - color: string; - - type: 'solidColor'; - - /** - * Control width and height of the solid color overlay. Supported transformations - * depend on the base/parent asset. See overlays on - * [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) - * and - * [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). - */ - transformation?: Array; -} - -export interface TextOverlayTransformation { - /** - * Specifies the transparency level of the text overlay. Accepts integers from `1` - * to `9`. - */ - alpha?: number; - - /** - * Specifies the background color of the text overlay. Accepts an RGB hex code, an - * RGBA code, or a color name. - */ - background?: string; - - /** - * Flip/mirror the text horizontally, vertically, or in both directions. Acceptable - * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or - * `v_h`. - */ - flip?: 'h' | 'v' | 'h_v' | 'v_h'; - - /** - * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., - * `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. - */ - fontColor?: string; - - /** - * Specifies the font family of the overlaid text. Choose from the supported fonts - * list or use a custom font. See - * [Supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) - * and - * [Custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). - */ - fontFamily?: string; - - /** - * Specifies the font size of the overlaid text. Accepts a numeric value or an - * arithmetic expression. - */ - fontSize?: number | string; - - /** - * Specifies the inner alignment of the text when width is more than the text - * length. - */ - innerAlignment?: 'left' | 'right' | 'center'; - - /** - * Specifies the line height for multi-line text overlays. It will come into effect - * only if the text wraps over multiple lines. Accepts either an integer value or - * an arithmetic expression. - */ - lineHeight?: number | string; - - /** - * Specifies the padding around the overlaid text. Can be provided as a single - * positive integer or multiple values separated by underscores (following CSS - * shorthand order). Arithmetic expressions are also accepted. - */ - padding?: number | string; - - /** - * Specifies the corner radius: - * - * - Single value (positive integer): Applied to all corners (e.g., `20`). - * - `max`: Creates a circular or oval shape. - * - Per-corner array: Provide four underscore-separated values representing - * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., - * `10_20_30_40`). See - * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). - */ - radius?: number | 'max' | string; - - /** - * Specifies the rotation angle of the text overlay. Accepts a numeric value for - * clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. - */ - rotation?: number | string; - - /** - * Specifies the typography style of the text. Supported values: - * - * - Single styles: `b` (bold), `i` (italic), `strikethrough`. - * - Combinations: Any combination separated by underscores, e.g., `b_i`, - * `b_i_strikethrough`. - */ - typography?: string; - - /** - * Specifies the maximum width (in pixels) of the overlaid text. The text wraps - * automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are - * supported. Useful when used in conjunction with the `background`. Learn about - * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - */ - width?: number | string; -} - - -/** - * Subtitle styling options. - * [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - * from the docs. - */ -export interface SubtitleOverlayTransformation { - /** - * Specifies the subtitle background color using a standard color name, an RGB - * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - background?: string; - - /** - * Sets the font color of the subtitle text using a standard color name, an RGB - * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - color?: string; - - /** - * Sets the font family of subtitle text. Refer to the - * [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) - * in the ImageKit transformations guide. - */ - fontFamily?: string; - - /** - * Sets the font outline of the subtitle text. Requires the outline width (an - * integer) and the outline color (as an RGB color code, RGBA color code, or - * standard web color name) separated by an underscore. Example: `fol-2_blue` - * (outline width of 2px and outline color blue), `fol-2_A1CCDD` (outline width of - * 2px and outline color `#A1CCDD`) and `fol-2_A1CCDD50` (outline width of 2px and - * outline color `#A1CCDD` at 50% opacity). - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - fontOutline?: string; - - /** - * Sets the font shadow for the subtitle text. Requires the shadow color (as an RGB - * color code, RGBA color code, or standard web color name) and shadow indent (an - * integer) separated by an underscore. Example: `fsh-blue_2` (shadow color blue, - * indent of 2px), `fsh-A1CCDD_3` (shadow color `#A1CCDD`, indent of 3px), - * `fsh-A1CCDD50_3` (shadow color `#A1CCDD` at 50% opacity, indent of 3px). - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - fontShadow?: string; - - /** - * Sets the font size of subtitle text. - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - fontSize?: number; - - /** - * Sets the typography style of the subtitle text. Supports values are `b` for - * bold, `i` for italics, and `b_i` for bold with italics. - * - * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) - */ - typography?: 'b' | 'i' | 'b_i'; -} - -export interface SolidColorOverlayTransformation { - /** - * Specifies the transparency level of the overlaid solid color layer. Supports - * integers from `1` to `9`. - */ - alpha?: number; - - /** - * Specifies the background color of the solid color overlay. Accepts an RGB hex - * code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. - */ - background?: string; - - /** - * Creates a linear gradient with two colors. Pass `true` for a default gradient, - * or provide a string for a custom gradient. Only works if the base asset is an - * image. See - * [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). - */ - gradient?: true | string; - - /** - * Controls the height of the solid color overlay. Accepts a numeric value or an - * arithmetic expression. Learn about - * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - */ - height?: number | string; - - /** - * Specifies the corner radius of the solid color overlay. - * - * - Single value (positive integer): Applied to all corners (e.g., `20`). - * - `max`: Creates a circular or oval shape. - * - Per-corner array: Provide four underscore-separated values representing - * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., - * `10_20_30_40`). See - * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). - */ - radius?: number | 'max' | string; - - /** - * Controls the width of the solid color overlay. Accepts a numeric value or an - * arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about - * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). - */ - width?: number | string; -} diff --git a/src/interfaces/UploadOptions.ts b/src/interfaces/UploadOptions.ts index 1eaed4b..e57fdc8 100644 --- a/src/interfaces/UploadOptions.ts +++ b/src/interfaces/UploadOptions.ts @@ -1,42 +1,4 @@ -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 { - /** - * Specifies pre-transformations to be applied. Must be a valid string of transformations like "w-300,h-300". - * Refer to the docs for more details on transformations. - * - * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#pre-transformation} - */ - pre?: string; - - /** - * Specifies post-transformations to be applied. This is an array of transformation objects, each with: - * - type: One of "transformation", "gif-to-video", "thumbnail", or "abs". - * - value: A valid transformation string required if "type" is "transformation" or "abs". Optional if "type" is "gif-to-video" or "thumbnail". - * - protocol: Used only when type is "abs". Can be "hls" or "dash". - * - * Refer to the docs for more details on transformations and usage in post. - * - * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload#post-transformation} - */ - post?: PostTransformation[]; -} +import * as Shared from './shared'; /** * Options used when uploading a file using the V1 API. @@ -45,143 +7,214 @@ interface Transformation { */ export interface UploadOptions { /** - * This field accepts three main input formats for the file content: - * - "binary": Directly pass the binary data. Typically used when uploading via the browser using a File or Blob. - * - "base64": A base64-encoded string of the file content. - * - "url": A direct URL from which ImageKit server will download the file and upload. + * 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: string | Blob | File; /** - * The name with which the file should be uploaded. - * - Valid characters: alphanumeric (a-z, A-Z, 0-9, including Unicode letters and numerals) and the special chars ". _ -" - * - Any other character (including space) is replaced with "_" + * The name with which the file has to be uploaded. The file name can contain: * - * Example: "company_logo.png" + * - Alphanumeric Characters: `a-z`, `A-Z`, `0-9`. + * - Special Characters: `.`, `-` + * + * Any other character including space will be replaced by `_` */ fileName: string; /** - * The HMAC-SHA1 digest of the concatenation of "token + expire". The signing key is your ImageKit private API key. - * Required for client-side authentication. Must be computed on the server side. - * Calculate this signature in your secure server and pass it to the client. + * 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. + * + * **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. */ - signature: string; + token: string; /** - * A unique value to identify and prevent replays. Typically a UUID (e.g., version 4). - * Each request must carry a fresh token. The server rejects reused tokens, even if the original request failed. + * Server-side checks to run on the asset. Read more about + * [Upload API checks](/docs/api-reference/upload-file/upload-file#upload-api-checks). */ - token: string; + checks?: string; /** - * A Unix timestamp in seconds, less than 1 hour in the future. + * 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. */ - expire: number; + customCoordinates?: string; /** - * The public API key of your ImageKit account. You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). + * JSON key-value pairs to associate with the asset. Create the custom metadata + * fields before setting these values. */ - publicKey: string; + customMetadata?: { [key: string]: unknown }; /** - * 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 + * Optional text to describe the contents of the file. */ - useUniqueFileName?: boolean; + description?: string; /** - * Optionally set tags on the uploaded file. - * If passing an array, the SDK automatically joins them into a comma-separated string when sending to the server. - * Example: ["t-shirt", "round-neck", "men"] => "t-shirt,round-neck,men" + * 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. */ - tags?: string | string[]; + expire: number; /** - * The folder path where the file will be stored, e.g., "/images/folder/". - * - If the path doesn't exist, it is created on-the-fly. - * - Nested folders are supported by using multiple "/". - * - Default: "/" + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ + extensions?: Shared.Extensions; + + /** + * The folder path in which the image has to be uploaded. If the folder(s) didn't + * exist before, a new folder(s) is created. + * + * The folder name can contain: + * + * - Alphanumeric Characters: `a-z` , `A-Z` , `0-9` + * - Special Characters: `/` , `_` , `-` + * + * Using multiple `/` creates a nested folder. */ folder?: string; /** - * Whether to mark the file as private (only relevant for image uploads). - * A private file requires signed URLs or named transformations for access. - * Default: false + * 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; /** - * A string in "x,y,width,height" format that defines the region of interest in an image (top-left coords and area size). - * Example: "10,10,100,100". + * 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. */ - customCoordinates?: string; + isPublished?: boolean; /** - * A comma-separated or array-based set of fields to return in the upload response. - * Example: "tags,customCoordinates,isPrivateFile,metadata" + * 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. */ - responseFields?: string | string[]; + overwriteAITags?: boolean; /** - * An array of extension objects to apply to the image, e.g. background removal, auto-tagging, etc. - * The SDK will JSON-stringify this array automatically before sending. + * If the request does not have `customMetadata`, and a file already exists at the + * exact location, existing customMetadata will be removed. */ - extensions?: object[]; + overwriteCustomMetadata?: boolean; /** - * A webhook URL to receive the final status of any pending extensions once they've completed processing. + * If `false` and `useUniqueFileName` is also `false`, and a file already exists at + * the exact location, upload API will return an error immediately. */ - webhookUrl?: string; + overwriteFile?: boolean; /** - * Defaults to true. If false, and "useUniqueFileName" is also false, the API immediately fails if a file with the same name/folder already exists. + * If the request does not have `tags`, and a file already exists at the exact + * location, existing tags will be removed. */ - overwriteFile?: boolean; + overwriteTags?: boolean; /** - * Defaults to true. If true, and an existing file is found at the same location, its AITags are removed. Set to false to keep existing AITags. + * The public API key of your ImageKit account. + * You can find it in the [ImageKit dashboard](https://imagekit.io/dashboard/developer/api-keys). */ - overwriteAITags?: boolean; + publicKey: string; /** - * Defaults to true. If no tags are specified in the request, existing tags are removed from overwritten files. Setting to false has no effect if the request includes tags. + * Array of response field keys to include in the API response body. */ - overwriteTags?: boolean; + responseFields?: Array< + | 'tags' + | 'customCoordinates' + | 'isPrivateFile' + | 'embeddedMetadata' + | 'isPublished' + | 'customMetadata' + | 'metadata' + | 'selectedFieldsSchema' + >; /** - * Defaults to true. If no customMetadata is specified in the request, existing customMetadata is removed from overwritten files. Setting to false has no effect if the request specifies customMetadata. + * The HMAC-SHA1 digest of the concatenation of "token + expire". The signing key is your ImageKit private API key. + * Calculate this signature in your secure server and pass it to the client. */ - overwriteCustomMetadata?: boolean; + signature: string; /** - * A stringified JSON or an object containing custom metadata fields to store with the file. - * Custom metadata fields must be pre-defined in your ImageKit configuration. + * 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. */ - customMetadata?: string | Record>; + tags?: Array; /** - * Defines pre and post transformations to be applied to the file during upload. The SDK enforces certain validation rules for pre/post transformations. - * For details, see: - * {@link https://imagekit.io/docs/dam/pre-and-post-transformation-on-upload} + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. */ - transformation?: Transformation; + transformation?: FileUploadParams.Transformation; /** - * An optional XMLHttpRequest instance for the upload. The SDK merges it with its own logic to handle progress events, etc. - * You can listen to `progress` or other events on this object for custom logic. + * 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. */ - xhr?: XMLHttpRequest; + useUniqueFileName?: boolean; /** - * A string specifying the checks to be performed server-side before uploading to the media library, e.g. size or mime type checks. - * For format details, see: {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#upload-api-checks} + * 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. */ - checks?: string; + webhookUrl?: string; + + // JS SDK specific options + + /** + * An optional XMLHttpRequest instance for the upload. The SDK merges it with its own logic to handle progress events, etc. + * You can listen to `progress` or other events on this object for custom logic. + */ + xhr?: XMLHttpRequest; /** * Optional callback function that will be called with the progress event when the file is being uploaded. @@ -194,3 +227,96 @@ export interface UploadOptions { */ abortSignal?: AbortSignal; } + +export namespace FileUploadParams { + /** + * Configure pre-processing (`pre`) and post-processing (`post`) transformations. + * + * - `pre` — applied before the file is uploaded to the Media Library. + * Useful for reducing file size or applying basic optimizations upfront (e.g., + * resize, compress). + * + * - `post` — applied immediately after upload. + * Ideal for generating transformed versions (like video encodes or thumbnails) + * in advance, so they're ready for delivery without delay. + * + * You can mix and match any combination of post-processing types. + */ + export interface Transformation { + /** + * List of transformations to apply _after_ the file is uploaded. + * Each item must match one of the following types: `transformation`, + * `gif-to-video`, `thumbnail`, `abs`. + */ + post?: Array< + | Transformation.Transformation + | Transformation.GifToVideo + | Transformation.Thumbnail + | Transformation.Abs + >; + + /** + * Transformation string to apply before uploading the file to the Media Library. + * Useful for optimizing files at ingestion. + */ + pre?: string; + } + + export namespace Transformation { + export interface Transformation { + /** + * Transformation type. + */ + type: 'transformation'; + + /** + * Transformation string (e.g. `w-200,h-200`). + * Same syntax as ImageKit URL-based transformations. + */ + value: string; + } + + export interface GifToVideo { + /** + * Converts an animated GIF into an MP4. + */ + type: 'gif-to-video'; + + /** + * Optional transformation string to apply to the output video. + * **Example**: `q-80` + */ + value?: string; + } + + export interface Thumbnail { + /** + * Generates a thumbnail image. + */ + type: 'thumbnail'; + + /** + * Optional transformation string. + * **Example**: `w-150,h-150` + */ + value?: string; + } + + export interface Abs { + /** + * Streaming protocol to use (`hls` or `dash`). + */ + protocol: 'hls' | 'dash'; + + /** + * Adaptive Bitrate Streaming (ABS) setup. + */ + type: 'abs'; + + /** + * List of different representations you want to create separated by an underscore. + */ + value: string; + } + } +} \ No newline at end of file diff --git a/src/interfaces/UploadResponse.ts b/src/interfaces/UploadResponse.ts index dbae9c0..2c9f4e8 100644 --- a/src/interfaces/UploadResponse.ts +++ b/src/interfaces/UploadResponse.ts @@ -1,97 +1,223 @@ /** - * 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. - * - * {@link https://imagekit.io/docs/api-reference/digital-asset-management-dam/list-and-search-assets} + * JSON object containing metadata. */ -type FileType = "all" | "image" | "non-image"; +export interface Metadata { + /** + * The audio codec used in the video (only for video). + */ + audioCodec?: string; -/** - * Metadata object structure - * - * {@link https://imagekit.io/docs/api-reference/file-metadata/get-uploaded-file-metadata#Responses} - * - * Contents of Object - * - * - Exif - * - * For more information about the Exif standard, please refer to the specification found on http://www.exif.org. A comprehensive list of available Exif attributes and their meaning can be found on http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/. - * - * - Perceptual Hash (pHash) - * - * Perceptual hashing allows you to construct a hash value that uniquely identifies an input image based on the image's contents. It is different from cryptographic hash functions like MD5 and SHA1. pHash provides similar hash value after minor distortions, like small rotations, blurring, and compression in the image. - */ -interface Metadata { - height: number; - width: number; - size: number; - format: string; - hasColorProfile: boolean; - quality: number; - density: number; - hasTransparency: boolean; - pHash: string; - 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: any; - }; + /** + * The bit rate of the video in kbps (only for video). + */ + bitRate?: number; + + /** + * The density of the image in DPI. + */ + density?: number; + + /** + * The duration of the video in seconds (only for video). + */ + duration?: number; + + exif?: Metadata.Exif; + + /** + * The format of the file (e.g., 'jpg', 'mp4'). + */ + format?: string; + + /** + * Indicates if the image has a color profile. + */ + hasColorProfile?: boolean; + + /** + * Indicates if the image contains transparent areas. + */ + hasTransparency?: boolean; + + /** + * The height of the image or video in pixels. + */ + height?: number; + + /** + * Perceptual hash of the image. + */ + pHash?: string; + + /** + * The quality indicator of the image. + */ + quality?: number; + + /** + * The file size in bytes. + */ + size?: number; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * The width of the image or video in pixels. + */ + width?: number; +} + +export namespace Metadata { + export interface Exif { + /** + * Object containing Exif details. + */ + exif?: Exif.Exif; + + /** + * Object containing GPS information. + */ + gps?: Exif.Gps; + + /** + * Object containing EXIF image information. + */ + image?: Exif.Image; + + /** + * JSON object. + */ + interoperability?: Exif.Interoperability; + + makernote?: { [key: string]: unknown }; + + /** + * Object containing Thumbnail information. + */ + thumbnail?: Exif.Thumbnail; + } + + export namespace Exif { + /** + * Object containing Exif details. + */ + export interface Exif { + ApertureValue?: number; + + ColorSpace?: number; + + CreateDate?: string; + + CustomRendered?: number; + + DateTimeOriginal?: string; + + ExifImageHeight?: number; + + ExifImageWidth?: number; + + ExifVersion?: string; + + ExposureCompensation?: number; + + ExposureMode?: number; + + ExposureProgram?: number; + + ExposureTime?: number; + + Flash?: number; + + FlashpixVersion?: string; + + FNumber?: number; + + FocalLength?: number; + + FocalPlaneResolutionUnit?: number; + + FocalPlaneXResolution?: number; + + FocalPlaneYResolution?: number; + + InteropOffset?: number; + + ISO?: number; + + MeteringMode?: number; + + SceneCaptureType?: number; + + ShutterSpeedValue?: number; + + SubSecTime?: string; + + WhiteBalance?: number; + } + + /** + * Object containing GPS information. + */ + export interface Gps { + GPSVersionID?: Array; + } + + /** + * Object containing EXIF image information. + */ + export interface Image { + ExifOffset?: number; + + GPSInfo?: number; + + Make?: string; + + Model?: string; + + ModifyDate?: string; + + Orientation?: number; + + ResolutionUnit?: number; + + Software?: string; + + XResolution?: number; + + YCbCrPositioning?: number; + + YResolution?: number; + } + + /** + * JSON object. + */ + export interface Interoperability { + InteropIndex?: string; + + InteropVersion?: string; + } + + /** + * Object containing Thumbnail information. + */ + export interface Thumbnail { + Compression?: number; + + ResolutionUnit?: number; + + ThumbnailLength?: number; + + ThumbnailOffset?: number; + + XResolution?: number; + + YResolution?: number; + } + } } export interface ResponseMetadata { @@ -101,72 +227,166 @@ export interface ResponseMetadata { } /** - * Response from server when file is uploaded successfully. - * - * {@link https://imagekit.io/docs/api-reference/upload-file/upload-file#Responses} + * Object containing details of a successful upload. */ export interface UploadResponse { /** - * Unique fileId. Store this fileld in your database, as this will be used to perform update action on this file. + * An array of tags assigned to the uploaded file by auto tagging. */ - fileId?: string; + AITags?: Array | null; + /** - * The name of the uploaded file. + * The audio codec used in the video (only for video). */ - name?: string; + audioCodec?: string; + /** - * The URL of the file. + * The bit rate of the video in kbps (only for video). */ - url?: string; + bitRate?: number; + /** - * In case of an image, a small thumbnail URL. + * 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. */ - thumbnailUrl?: string; + customCoordinates?: string | null; + /** - * Height of the uploaded image file. Only applicable when file type is image. + * 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. */ - height?: number; + customMetadata?: { [key: string]: unknown }; + /** - * Width of the uploaded image file. Only applicable when file type is image. + * Optional text to describe the contents of the file. Can be set by the user or + * the ai-auto-description extension. */ - width?: number; + description?: string; + /** - * Size of the uploaded file in bytes. + * The duration of the video in seconds (only for video). */ - size?: number; + duration?: number; + + /** + * Consolidated embedded metadata associated with the file. It includes exif, iptc, + * and xmp data. Send `embeddedMetadata` in `responseFields` in API request to get + * embeddedMetadata in the upload API response. + */ + embeddedMetadata?: { [key: string]: unknown }; + /** - * Type of file. It can either be image or non-image. + * 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. */ - fileType?: FileType; + extensionStatus?: FileUploadResponse.ExtensionStatus; + + /** + * Unique fileId. Store this fileld in your database, as this will be used to + * perform update action on this file. + */ + fileId?: string; + /** - * The path of the file uploaded. It includes any folder that you specified while uploading. + * The relative path of the file in the media library e.g. + * `/marketing-assets/new-banner.jpg`. */ filePath?: string; + /** - * Array of tags associated with the image. + * Type of the uploaded file. Possible values are `image`, `non-image`. */ - tags?: string[]; + fileType?: string; + + /** + * Height of the image in pixels (Only for images) + */ + height?: number; + /** - * Is the file marked as private. It can be either true or false. + * 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; + /** - * Value of custom coordinates associated with the image in format x,y,width,height. + * 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. */ - customCoordinates?: string | null; + isPublished?: boolean; + /** - * The metadata of the upload file. Use responseFields property in request to get the metadata returned in response of upload API. + * Legacy metadata. Send `metadata` in `responseFields` in API request to get + * metadata in the upload API response. */ metadata?: Metadata; - /* - * AITags field is populated only because the google-auto-tagging extension was executed synchronously and it received a successresponse. + + /** + * Name of the asset. */ - AITags?: object[]; + name?: string; - /* - * Field object which will contain the status of each extension at the time of completion of the update/upload request. - */ - extensionStatus?: { [key: string]: string } + /** + * This field is included in the response only if the Path policy feature is + * available in the plan. It contains schema definitions for the custom metadata + * fields selected for the specified file path. Field selection can only be done + * when the Path policy feature is enabled. + * + * Keys are the names of the custom metadata fields; the value object has details + * about the custom metadata schema. + */ + selectedFieldsSchema?: { [key: string]: FileUploadResponse.SelectedFieldsSchema }; + + /** + * Size of the image file in Bytes. + */ + size?: number; + + /** + * The array of tags associated with the asset. If no tags are set, it will be + * `null`. Send `tags` in `responseFields` in API request to get the value of this + * field. + */ + tags?: Array | null; + + /** + * In the case of an image, a small thumbnail URL. + */ + thumbnailUrl?: string; + + /** + * A publicly accessible URL of the file. + */ + url?: string; + + /** + * An object containing the file or file version's `id` (versionId) and `name`. + */ + versionInfo?: FileUploadResponse.VersionInfo; + + /** + * The video codec used in the video (only for video). + */ + videoCodec?: string; + + /** + * Width of the image in pixels (Only for Images) + */ + width?: number; /** * Message indicating that the file upload is accepted. This field is only present when the upload is accepted but not yet processed. @@ -180,3 +400,122 @@ export interface UploadResponse { */ readonly $ResponseMetadata: ResponseMetadata; } + +export namespace FileUploadResponse { + export interface AITag { + /** + * Confidence score of the tag. + */ + confidence?: number; + + /** + * Name of the tag. + */ + name?: string; + + /** + * Array of `AITags` associated with the image. If no `AITags` are set, it will be + * null. These tags can be added using the `google-auto-tagging` or + * `aws-auto-tagging` extensions. + */ + source?: string; + } + + /** + * Extension names with their processing status at the time of completion of the + * request. It could have one of the following status values: + * + * `success`: The extension has been successfully applied. `failed`: The extension + * has failed and will not be retried. `pending`: The extension will finish + * processing in some time. On completion, the final status (success / failed) will + * be sent to the `webhookUrl` provided. + * + * If no extension was requested, then this parameter is not returned. + */ + export interface ExtensionStatus { + 'ai-auto-description'?: 'success' | 'pending' | 'failed'; + + 'ai-tasks'?: 'success' | 'pending' | 'failed'; + + 'aws-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'google-auto-tagging'?: 'success' | 'pending' | 'failed'; + + 'remove-bg'?: 'success' | 'pending' | 'failed'; + } + + export interface SelectedFieldsSchema { + /** + * Type of the custom metadata field. + */ + type: 'Text' | 'Textarea' | 'Number' | 'Date' | 'Boolean' | 'SingleSelect' | 'MultiSelect'; + + /** + * The default value for this custom metadata field. The value should match the + * `type` of custom metadata field. + */ + defaultValue?: string | number | boolean | Array; + + /** + * Specifies if the custom metadata field is required or not. + */ + isValueRequired?: boolean; + + /** + * Maximum length of string. Only set if `type` is set to `Text` or `Textarea`. + */ + maxLength?: number; + + /** + * Maximum value of the field. Only set if field type is `Date` or `Number`. For + * `Date` type field, the value will be in ISO8601 string format. For `Number` type + * field, it will be a numeric value. + */ + maxValue?: string | number; + + /** + * Minimum length of string. Only set if `type` is set to `Text` or `Textarea`. + */ + minLength?: number; + + /** + * Minimum value of the field. Only set if field type is `Date` or `Number`. For + * `Date` type field, the value will be in ISO8601 string format. For `Number` type + * field, it will be a numeric value. + */ + minValue?: string | number; + + /** + * Indicates whether the custom metadata field is read only. A read only field + * cannot be modified after being set. This field is configurable only via the + * **Path policy** feature. + */ + readOnly?: boolean; + + /** + * An array of allowed values when field type is `SingleSelect` or `MultiSelect`. + */ + selectOptions?: Array; + + /** + * Specifies if the selectOptions array is truncated. It is truncated when number + * of options are > 100. + */ + selectOptionsTruncated?: boolean; + } + + /** + * 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; + } +} \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 5aacf10..639daaf 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,7 +1,6 @@ // src/interfaces/index.ts // Re-export all interfaces so that TypeDoc includes referenced types in the documentation +export * from './shared'; export * from './UploadResponse'; -export * from './UploadOptions'; -export * from './Transformation'; -export * from './SrcOptions'; +export * from './UploadOptions'; \ No newline at end of file diff --git a/src/interfaces/shared.ts b/src/interfaces/shared.ts new file mode 100644 index 0000000..ad8245e --- /dev/null +++ b/src/interfaces/shared.ts @@ -0,0 +1,1725 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +export interface BaseOverlay { + /** + * Controls how the layer blends with the base image or underlying content. Maps to + * `lm` in the URL. By default, layers completely cover the base image beneath + * them. Layer modes change this behavior: + * + * - `multiply`: Multiplies the pixel values of the layer with the base image. The + * result is always darker than the original images. This is ideal for applying + * shadows or color tints. + * - `displace`: Uses the layer as a displacement map to distort pixels in the base + * image. The red channel controls horizontal displacement, and the green channel + * controls vertical displacement. Requires `x` or `y` parameter to control + * displacement magnitude. + * - `cutout`: Acts as an inverse mask where opaque areas of the layer turn the + * base image transparent, while transparent areas leave the base image + * unchanged. This mode functions like a hole-punch, effectively cutting the + * shape of the layer out of the underlying image. + * - `cutter`: Acts as a shape mask where only the parts of the base image that + * fall inside the opaque area of the layer are preserved. This mode functions + * like a cookie-cutter, trimming the base image to match the specific dimensions + * and shape of the layer. See + * [Layer modes](https://imagekit.io/docs/add-overlays-on-images#layer-modes). + */ + layerMode?: 'multiply' | 'cutter' | 'cutout' | 'displace'; + + /** + * Specifies the overlay's position relative to the parent asset. See + * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). + */ + position?: OverlayPosition; + + /** + * Specifies timing information for the overlay (only applicable if the base asset + * is a video). See + * [Position of Layer](https://imagekit.io/docs/transformations#position-of-layer). + */ + timing?: OverlayTiming; +} + +/** + * Configuration object for an extension (base extensions only, not saved extension + * references). + */ +export type ExtensionConfig = + | ExtensionConfig.RemoveBg + | ExtensionConfig.AutoTaggingExtension + | ExtensionConfig.AIAutoDescription + | ExtensionConfig.AITasks; + +export namespace ExtensionConfig { + export interface RemoveBg { + /** + * Specifies the background removal extension. + */ + name: 'remove-bg'; + + options?: RemoveBg.Options; + } + + export namespace RemoveBg { + export interface Options { + /** + * Whether to add an artificial shadow to the result. Default is false. Note: + * Adding shadows is currently only supported for car photos. + */ + add_shadow?: boolean; + + /** + * Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or + * color name (e.g., "green"). If this parameter is set, `bg_image_url` must be + * empty. + */ + bg_color?: string; + + /** + * Sets a background image from a URL. If this parameter is set, `bg_color` must be + * empty. + */ + bg_image_url?: string; + + /** + * Allows semi-transparent regions in the result. Default is true. Note: + * Semitransparency is currently only supported for car windows. + */ + semitransparency?: boolean; + } + } + + export interface AutoTaggingExtension { + /** + * Maximum number of tags to attach to the asset. + */ + maxTags: number; + + /** + * Minimum confidence level for tags to be considered valid. + */ + minConfidence: number; + + /** + * Specifies the auto-tagging extension used. + */ + name: 'google-auto-tagging' | 'aws-auto-tagging'; + } + + export interface AIAutoDescription { + /** + * Specifies the auto description extension. + */ + name: 'ai-auto-description'; + } + + export interface AITasks { + /** + * Specifies the AI tasks extension for automated image analysis using AI models. + */ + name: 'ai-tasks'; + + /** + * Array of task objects defining AI operations to perform on the asset. + */ + tasks: Array; + } + + export namespace AITasks { + export interface SelectTags { + /** + * The question or instruction for the AI to analyze the image. + */ + instruction: string; + + /** + * Task type that analyzes the image and adds matching tags from a vocabulary. + */ + type: 'select_tags'; + + /** + * Array of possible tag values. Combined length of all strings must not exceed 500 + * characters. Cannot contain the `%` character. + */ + vocabulary: Array; + + /** + * Maximum number of tags to select from the vocabulary. + */ + max_selections?: number; + + /** + * Minimum number of tags to select from the vocabulary. + */ + min_selections?: number; + } + + export interface SelectMetadata { + /** + * Name of the custom metadata field to set. The field must exist in your account. + */ + field: string; + + /** + * The question or instruction for the AI to analyze the image. + */ + instruction: string; + + /** + * Task type that analyzes the image and sets a custom metadata field value from a + * vocabulary. + */ + type: 'select_metadata'; + + /** + * Maximum number of values to select from the vocabulary. + */ + max_selections?: number; + + /** + * Minimum number of values to select from the vocabulary. + */ + min_selections?: number; + + /** + * Array of possible values matching the custom metadata field type. + */ + vocabulary?: Array; + } + + export interface YesNo { + /** + * The yes/no question for the AI to answer about the image. + */ + instruction: string; + + /** + * Task type that asks a yes/no question and executes actions based on the answer. + */ + type: 'yes_no'; + + /** + * Actions to execute if the AI answers no. + */ + on_no?: YesNo.OnNo; + + /** + * Actions to execute if the AI cannot determine the answer. + */ + on_unknown?: YesNo.OnUnknown; + + /** + * Actions to execute if the AI answers yes. + */ + on_yes?: YesNo.OnYes; + } + + export namespace YesNo { + /** + * Actions to execute if the AI answers no. + */ + export interface OnNo { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnNo { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + + /** + * Actions to execute if the AI cannot determine the answer. + */ + export interface OnUnknown { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnUnknown { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + + /** + * Actions to execute if the AI answers yes. + */ + export interface OnYes { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnYes { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + } + } +} + +/** + * Array of extensions to be applied to the asset. Each extension can be configured + * with specific parameters based on the extension type. + */ +export type Extensions = Array< + | Extensions.RemoveBg + | Extensions.AutoTaggingExtension + | Extensions.AIAutoDescription + | Extensions.AITasks + | Extensions.SavedExtension +>; + +export namespace Extensions { + export interface RemoveBg { + /** + * Specifies the background removal extension. + */ + name: 'remove-bg'; + + options?: RemoveBg.Options; + } + + export namespace RemoveBg { + export interface Options { + /** + * Whether to add an artificial shadow to the result. Default is false. Note: + * Adding shadows is currently only supported for car photos. + */ + add_shadow?: boolean; + + /** + * Specifies a solid color background using hex code (e.g., "81d4fa", "fff") or + * color name (e.g., "green"). If this parameter is set, `bg_image_url` must be + * empty. + */ + bg_color?: string; + + /** + * Sets a background image from a URL. If this parameter is set, `bg_color` must be + * empty. + */ + bg_image_url?: string; + + /** + * Allows semi-transparent regions in the result. Default is true. Note: + * Semitransparency is currently only supported for car windows. + */ + semitransparency?: boolean; + } + } + + export interface AutoTaggingExtension { + /** + * Maximum number of tags to attach to the asset. + */ + maxTags: number; + + /** + * Minimum confidence level for tags to be considered valid. + */ + minConfidence: number; + + /** + * Specifies the auto-tagging extension used. + */ + name: 'google-auto-tagging' | 'aws-auto-tagging'; + } + + export interface AIAutoDescription { + /** + * Specifies the auto description extension. + */ + name: 'ai-auto-description'; + } + + export interface AITasks { + /** + * Specifies the AI tasks extension for automated image analysis using AI models. + */ + name: 'ai-tasks'; + + /** + * Array of task objects defining AI operations to perform on the asset. + */ + tasks: Array; + } + + export namespace AITasks { + export interface SelectTags { + /** + * The question or instruction for the AI to analyze the image. + */ + instruction: string; + + /** + * Task type that analyzes the image and adds matching tags from a vocabulary. + */ + type: 'select_tags'; + + /** + * Array of possible tag values. Combined length of all strings must not exceed 500 + * characters. Cannot contain the `%` character. + */ + vocabulary: Array; + + /** + * Maximum number of tags to select from the vocabulary. + */ + max_selections?: number; + + /** + * Minimum number of tags to select from the vocabulary. + */ + min_selections?: number; + } + + export interface SelectMetadata { + /** + * Name of the custom metadata field to set. The field must exist in your account. + */ + field: string; + + /** + * The question or instruction for the AI to analyze the image. + */ + instruction: string; + + /** + * Task type that analyzes the image and sets a custom metadata field value from a + * vocabulary. + */ + type: 'select_metadata'; + + /** + * Maximum number of values to select from the vocabulary. + */ + max_selections?: number; + + /** + * Minimum number of values to select from the vocabulary. + */ + min_selections?: number; + + /** + * Array of possible values matching the custom metadata field type. + */ + vocabulary?: Array; + } + + export interface YesNo { + /** + * The yes/no question for the AI to answer about the image. + */ + instruction: string; + + /** + * Task type that asks a yes/no question and executes actions based on the answer. + */ + type: 'yes_no'; + + /** + * Actions to execute if the AI answers no. + */ + on_no?: YesNo.OnNo; + + /** + * Actions to execute if the AI cannot determine the answer. + */ + on_unknown?: YesNo.OnUnknown; + + /** + * Actions to execute if the AI answers yes. + */ + on_yes?: YesNo.OnYes; + } + + export namespace YesNo { + /** + * Actions to execute if the AI answers no. + */ + export interface OnNo { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnNo { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + + /** + * Actions to execute if the AI cannot determine the answer. + */ + export interface OnUnknown { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnUnknown { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + + /** + * Actions to execute if the AI answers yes. + */ + export interface OnYes { + /** + * Array of tag strings to add to the asset. + */ + add_tags?: Array; + + /** + * Array of tag strings to remove from the asset. + */ + remove_tags?: Array; + + /** + * Array of custom metadata field updates. + */ + set_metadata?: Array; + + /** + * Array of custom metadata fields to remove. + */ + unset_metadata?: Array; + } + + export namespace OnYes { + export interface SetMetadata { + /** + * Name of the custom metadata field to set. + */ + field: string; + + /** + * Value to set for the custom metadata field. The value type should match the + * custom metadata field type. + */ + value: string | number | boolean | Array; + } + + export interface UnsetMetadata { + /** + * Name of the custom metadata field to remove. + */ + field: string; + } + } + } + } + + export interface SavedExtension { + /** + * The unique ID of the saved extension to apply. + */ + id: string; + + /** + * Indicates this is a reference to a saved extension. + */ + name: 'saved-extension'; + } +} + +/** + * Options for generating responsive image attributes including `src`, `srcSet`, + * and `sizes` for HTML `` elements. This schema extends `SrcOptions` to add + * support for responsive image generation with breakpoints. + */ +export interface GetImageAttributesOptions extends SrcOptions { + /** + * Custom list of **device-width breakpoints** in pixels. These define common + * screen widths for responsive image generation. + * + * Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. Sorted + * automatically. + */ + deviceBreakpoints?: Array; + + /** + * Custom list of **image-specific breakpoints** in pixels. Useful for generating + * small variants (e.g., placeholders or thumbnails). + * + * Merged with `deviceBreakpoints` before calculating `srcSet`. Defaults to + * `[16, 32, 48, 64, 96, 128, 256, 384]`. Sorted automatically. + */ + imageBreakpoints?: Array; + + /** + * The value for the HTML `sizes` attribute (e.g., `"100vw"` or + * `"(min-width:768px) 50vw, 100vw"`). + * + * - If it includes one or more `vw` units, breakpoints smaller than the + * corresponding percentage of the smallest device width are excluded. + * - If it contains no `vw` units, the full breakpoint list is used. + * + * Enables a width-based strategy and generates `w` descriptors in `srcSet`. + */ + sizes?: string; + + /** + * The intended display width of the image in pixels, used **only when the `sizes` + * attribute is not provided**. + * + * Triggers a DPR-based strategy (1x and 2x variants) and generates `x` descriptors + * in `srcSet`. + * + * Ignored if `sizes` is present. + */ + width?: number; +} + +export interface ImageOverlay extends BaseOverlay { + /** + * Specifies the relative path to the image used as an overlay. + */ + input: string; + + type: 'image'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + * + * Regardless of the encoding method: + * + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain + * text. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Array of transformations to be applied to the overlay image. Supported + * transformations depends on the base/parent asset. See overlays on + * [Images](https://imagekit.io/docs/add-overlays-on-images#list-of-supported-image-transformations-in-image-layers) + * and + * [Videos](https://imagekit.io/docs/add-overlays-on-videos#list-of-transformations-supported-on-image-overlay). + */ + transformation?: Array; +} + +/** + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + */ +export type Overlay = TextOverlay | ImageOverlay | VideoOverlay | SubtitleOverlay | SolidColorOverlay; + +export interface OverlayPosition { + /** + * Specifies the position of the overlay relative to the parent image or video. + * Maps to `lfo` in the URL. + */ + focus?: + | 'center' + | 'top' + | 'left' + | 'bottom' + | 'right' + | 'top_left' + | 'top_right' + | 'bottom_left' + | 'bottom_right'; + + /** + * Specifies the x-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bw_mul_0.4` or `bw_sub_cw`. Maps to `lx` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + x?: number | string; + + /** + * Specifies the y-coordinate of the top-left corner of the base asset where the + * overlay's top-left corner will be positioned. It also accepts arithmetic + * expressions such as `bh_mul_0.4` or `bh_sub_ch`. Maps to `ly` in the URL. Learn + * about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + y?: number | string; +} + +export interface OverlayTiming { + /** + * Specifies the duration (in seconds) during which the overlay should appear on + * the base video. Accepts a positive number up to two decimal places (e.g., `20` + * or `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `ldu` in the URL. + */ + duration?: number | string; + + /** + * Specifies the end time (in seconds) for when the overlay should disappear from + * the base video. If both end and duration are provided, duration is ignored. + * Accepts a positive number up to two decimal places (e.g., `20` or `20.50`) and + * arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. Applies only if + * the base asset is a video. Maps to `leo` in the URL. + */ + end?: number | string; + + /** + * Specifies the start time (in seconds) for when the overlay should appear on the + * base video. Accepts a positive number up to two decimal places (e.g., `20` or + * `20.50`) and arithmetic expressions such as `bdu_mul_0.4` or `bdu_sub_idu`. + * Applies only if the base asset is a video. Maps to `lso` in the URL. + */ + start?: number | string; +} + +/** + * Resulting set of attributes suitable for an HTML `` element. Useful for + * enabling responsive image loading with `srcSet` and `sizes`. + */ +export interface ResponsiveImageAttributes { + /** + * URL for the _largest_ candidate (assigned to plain `src`). + */ + src: string; + + /** + * `sizes` returned (or synthesised as `100vw`). The value for the HTML `sizes` + * attribute. + */ + sizes?: string; + + /** + * Candidate set with `w` or `x` descriptors. Multiple image URLs separated by + * commas, each with a descriptor. + */ + srcSet?: string; + + /** + * Width as a number (if `width` was provided in the input options). + */ + width?: number; +} + +/** + * Saved extension object containing extension configuration. + */ +export interface SavedExtension { + /** + * Unique identifier of the saved extension. + */ + id?: string; + + /** + * Configuration object for an extension (base extensions only, not saved extension + * references). + */ + config?: ExtensionConfig; + + /** + * Timestamp when the saved extension was created. + */ + createdAt?: string; + + /** + * Description of the saved extension. + */ + description?: string; + + /** + * Name of the saved extension. + */ + name?: string; + + /** + * Timestamp when the saved extension was last updated. + */ + updatedAt?: string; +} + +export interface SolidColorOverlay extends BaseOverlay { + /** + * Specifies the color of the block using an RGB hex code (e.g., `FF0000`), an RGBA + * code (e.g., `FFAABB50`), or a color name (e.g., `red`). If an 8-character value + * is provided, the last two characters represent the opacity level (from `00` for + * 0.00 to `99` for 0.99). + */ + color: string; + + type: 'solidColor'; + + /** + * Control width and height of the solid color overlay. Supported transformations + * depend on the base/parent asset. See overlays on + * [Images](https://imagekit.io/docs/add-overlays-on-images#apply-transformation-on-solid-color-overlay) + * and + * [Videos](https://imagekit.io/docs/add-overlays-on-videos#apply-transformations-on-solid-color-block-overlay). + */ + transformation?: Array; +} + +export interface SolidColorOverlayTransformation { + /** + * Specifies the transparency level of the overlaid solid color layer. Supports + * integers from `1` to `9`. + */ + alpha?: number; + + /** + * Specifies the background color of the solid color overlay. Accepts an RGB hex + * code (e.g., `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + */ + background?: string; + + /** + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. Only works if the base asset is an + * image. See + * [gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + */ + gradient?: true | string; + + /** + * Controls the height of the solid color overlay. Accepts a numeric value or an + * arithmetic expression. Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + height?: number | string; + + /** + * Specifies the corner radius of the solid color overlay. + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max' | string; + + /** + * Controls the width of the solid color overlay. Accepts a numeric value or an + * arithmetic expression (e.g., `bw_mul_0.2` or `bh_div_2`). Learn about + * [arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + width?: number | string; +} + +/** + * Options for generating ImageKit URLs with transformations. See the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ +export interface SrcOptions { + /** + * Accepts a relative or absolute path of the resource. If a relative path is + * provided, it is appended to the `urlEndpoint`. If an absolute path is provided, + * `urlEndpoint` is ignored. + */ + src: string; + + /** + * Get your urlEndpoint from the + * [ImageKit dashboard](https://imagekit.io/dashboard/url-endpoints). + */ + urlEndpoint: string; + + /** + * These are additional query parameters that you want to add to the final URL. + * They can be any query parameters and not necessarily related to ImageKit. This + * is especially useful if you want to add a versioning parameter to your URLs. + */ + queryParameters?: { [key: string]: string | number }; + + /** + * An array of objects specifying the transformations to be applied in the URL. If + * more than one transformation is specified, they are applied in the order they + * are specified as chained transformations. See + * [Chained transformations](https://imagekit.io/docs/transformations#chained-transformations). + */ + transformation?: Array; + + /** + * By default, the transformation string is added as a query parameter in the URL, + * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the + * path of the URL, set this to `path`. Learn more in the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ + transformationPosition?: TransformationPosition; +} + +/** + * Available streaming resolutions for + * [adaptive bitrate streaming](https://imagekit.io/docs/adaptive-bitrate-streaming) + */ +export type StreamingResolution = '240' | '360' | '480' | '720' | '1080' | '1440' | '2160'; + +export interface SubtitleOverlay extends BaseOverlay { + /** + * Specifies the relative path to the subtitle file used as an overlay. + */ + input: string; + + type: 'subtitle'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + * + * Regardless of the encoding method: + * + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain + * text. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Control styling of the subtitle. See + * [Styling subtitles](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer). + */ + transformation?: Array; +} + +/** + * Subtitle styling options. + * [Learn more](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + * from the docs. + */ +export interface SubtitleOverlayTransformation { + /** + * Specifies the subtitle background color using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + background?: string; + + /** + * Sets the font color of the subtitle text using a standard color name, an RGB + * color code (e.g., FF0000), or an RGBA color code (e.g., FFAABB50). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + color?: string; + + /** + * Sets the font family of subtitle text. Refer to the + * [supported fonts documented](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + * in the ImageKit transformations guide. + */ + fontFamily?: string; + + /** + * Sets the font outline of the subtitle text. Requires the outline width (an + * integer) and the outline color (as an RGB color code, RGBA color code, or + * standard web color name) separated by an underscore. Example: `fol-2_blue` + * (outline width of 2px and outline color blue), `fol-2_A1CCDD` (outline width of + * 2px and outline color `#A1CCDD`) and `fol-2_A1CCDD50` (outline width of 2px and + * outline color `#A1CCDD` at 50% opacity). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontOutline?: string; + + /** + * Sets the font shadow for the subtitle text. Requires the shadow color (as an RGB + * color code, RGBA color code, or standard web color name) and shadow indent (an + * integer) separated by an underscore. Example: `fsh-blue_2` (shadow color blue, + * indent of 2px), `fsh-A1CCDD_3` (shadow color `#A1CCDD`, indent of 3px), + * `fsh-A1CCDD50_3` (shadow color `#A1CCDD` at 50% opacity, indent of 3px). + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontShadow?: string; + + /** + * Sets the font size of subtitle text. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + fontSize?: number; + + /** + * Sets the typography style of the subtitle text. Supports values are `b` for + * bold, `i` for italics, and `b_i` for bold with italics. + * + * [Subtitle styling options](https://imagekit.io/docs/add-overlays-on-videos#styling-controls-for-subtitles-layer) + */ + typography?: 'b' | 'i' | 'b_i'; +} + +export interface TextOverlay extends BaseOverlay { + /** + * Specifies the text to be displayed in the overlay. The SDK automatically handles + * special characters and encoding. + */ + text: string; + + type: 'text'; + + /** + * Text can be included in the layer as either `i-{input}` (plain text) or + * `ie-{base64_encoded_input}` (base64). By default, the SDK selects the + * appropriate format based on the input text. To always use base64 + * (`ie-{base64}`), set this parameter to `base64`. To always use plain text + * (`i-{input}`), set it to `plain`. + * + * Regardless of the encoding method, the input text is always percent-encoded to + * ensure it is URL-safe. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Control styling of the text overlay. See + * [Text overlays](https://imagekit.io/docs/add-overlays-on-images#text-overlay). + */ + transformation?: Array; +} + +export interface TextOverlayTransformation { + /** + * Specifies the transparency level of the text overlay. Accepts integers from `1` + * to `9`. + */ + alpha?: number; + + /** + * Specifies the background color of the text overlay. Accepts an RGB hex code, an + * RGBA code, or a color name. + */ + background?: string; + + /** + * Flip/mirror the text horizontally, vertically, or in both directions. Acceptable + * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or + * `v_h`. + */ + flip?: 'h' | 'v' | 'h_v' | 'v_h'; + + /** + * Specifies the font color of the overlaid text. Accepts an RGB hex code (e.g., + * `FF0000`), an RGBA code (e.g., `FFAABB50`), or a color name. + */ + fontColor?: string; + + /** + * Specifies the font family of the overlaid text. Choose from the supported fonts + * list or use a custom font. See + * [Supported fonts](https://imagekit.io/docs/add-overlays-on-images#supported-text-font-list) + * and + * [Custom font](https://imagekit.io/docs/add-overlays-on-images#change-font-family-in-text-overlay). + */ + fontFamily?: string; + + /** + * Specifies the font size of the overlaid text. Accepts a numeric value or an + * arithmetic expression. + */ + fontSize?: number | string; + + /** + * Specifies the inner alignment of the text when width is more than the text + * length. + */ + innerAlignment?: 'left' | 'right' | 'center'; + + /** + * Specifies the line height for multi-line text overlays. It will come into effect + * only if the text wraps over multiple lines. Accepts either an integer value or + * an arithmetic expression. + */ + lineHeight?: number | string; + + /** + * Specifies the padding around the overlaid text. Can be provided as a single + * positive integer or multiple values separated by underscores (following CSS + * shorthand order). Arithmetic expressions are also accepted. + */ + padding?: number | string; + + /** + * Specifies the corner radius: + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max' | string; + + /** + * Specifies the rotation angle of the text overlay. Accepts a numeric value for + * clockwise rotation or a string prefixed with "N" for counter-clockwise rotation. + */ + rotation?: number | string; + + /** + * Specifies the typography style of the text. Supported values: + * + * - Single styles: `b` (bold), `i` (italic), `strikethrough`. + * - Combinations: Any combination separated by underscores, e.g., `b_i`, + * `b_i_strikethrough`. + */ + typography?: string; + + /** + * Specifies the maximum width (in pixels) of the overlaid text. The text wraps + * automatically, and arithmetic expressions (e.g., `bw_mul_0.2` or `bh_div_2`) are + * supported. Useful when used in conjunction with the `background`. Learn about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + */ + width?: number | string; +} + +/** + * The SDK provides easy-to-use names for transformations. These names are + * converted to the corresponding transformation string before being added to the + * URL. SDKs are updated regularly to support new transformations. If you want to + * use a transformation that is not supported by the SDK, You can use the `raw` + * parameter to pass the transformation string directly. See the + * [Transformations documentation](https://imagekit.io/docs/transformations). + */ +export interface Transformation { + /** + * Uses AI to change the background. Provide a text prompt or a base64-encoded + * prompt, e.g., `prompt-snow road` or `prompte-[urlencoded_base64_encoded_text]`. + * Not supported inside overlay. See + * [AI Change Background](https://imagekit.io/docs/ai-transformations#change-background-e-changebg). + */ + aiChangeBackground?: string; + + /** + * Adds an AI-based drop shadow around a foreground object on a transparent or + * removed background. Optionally, control the direction, elevation, and saturation + * of the light source (e.g., `az-45` to change light direction). Pass `true` for + * the default drop shadow, or provide a string for a custom drop shadow. Supported + * inside overlay. See + * [AI Drop Shadow](https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow). + */ + aiDropShadow?: true | string; + + /** + * Uses AI to edit images based on a text prompt. Provide a text prompt or a + * base64-encoded prompt, e.g., `prompt-snow road` or + * `prompte-[urlencoded_base64_encoded_text]`. Not supported inside overlay. + * See [AI Edit](https://imagekit.io/docs/ai-transformations#edit-image-e-edit). + */ + aiEdit?: string; + + /** + * Applies ImageKit's in-house background removal. Supported inside overlay. See + * [AI Background Removal](https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove). + */ + aiRemoveBackground?: true; + + /** + * Uses third-party background removal. Note: It is recommended to use + * aiRemoveBackground, ImageKit's in-house solution, which is more cost-effective. + * Supported inside overlay. See + * [External Background Removal](https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg). + */ + aiRemoveBackgroundExternal?: true; + + /** + * Performs AI-based retouching to improve faces or product shots. Not supported + * inside overlay. See + * [AI Retouch](https://imagekit.io/docs/ai-transformations#retouch-e-retouch). + */ + aiRetouch?: true; + + /** + * Upscales images beyond their original dimensions using AI. Not supported inside + * overlay. See + * [AI Upscale](https://imagekit.io/docs/ai-transformations#upscale-e-upscale). + */ + aiUpscale?: true; + + /** + * Generates a variation of an image using AI. This produces a new image with + * slight variations from the original, such as changes in color, texture, and + * other visual elements, while preserving the structure and essence of the + * original image. Not supported inside overlay. See + * [AI Generate Variations](https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar). + */ + aiVariation?: true; + + /** + * Specifies the aspect ratio for the output, e.g., "ar-4-3". Typically used with + * either width or height (but not both). For example: aspectRatio = `4:3`, `4_3`, + * or an expression like `iar_div_2`. See + * [Image resize and crop – Aspect ratio](https://imagekit.io/docs/image-resize-and-crop#aspect-ratio---ar). + */ + aspectRatio?: number | string; + + /** + * Specifies the audio codec, e.g., `aac`, `opus`, or `none`. See + * [Audio codec](https://imagekit.io/docs/video-optimization#audio-codec---ac). + */ + audioCodec?: 'aac' | 'opus' | 'none'; + + /** + * Specifies the background to be used in conjunction with certain cropping + * strategies when resizing an image. + * + * - A solid color: e.g., `red`, `F3F3F3`, `AAFF0010`. See + * [Solid color background](https://imagekit.io/docs/effects-and-enhancements#solid-color-background). + * - Dominant color: `dominant` extracts the dominant color from the image. See + * [Dominant color background](https://imagekit.io/docs/effects-and-enhancements#dominant-color-background). + * - Gradient: `gradient_dominant` or `gradient_dominant_2` creates a gradient + * using the dominant colors. Optionally specify palette size (2 or 4), e.g., + * `gradient_dominant_4`. See + * [Gradient background](https://imagekit.io/docs/effects-and-enhancements#gradient-background). + * - A blurred background: e.g., `blurred`, `blurred_25_N15`, etc. See + * [Blurred background](https://imagekit.io/docs/effects-and-enhancements#blurred-background). + * - Expand the image boundaries using generative fill: `genfill`. Not supported + * inside overlay. Optionally, control the background scene by passing a text + * prompt: `genfill[:-prompt-${text}]` or + * `genfill[:-prompte-${urlencoded_base64_encoded_text}]`. See + * [Generative fill background](https://imagekit.io/docs/ai-transformations#generative-fill-bg-genfill). + */ + background?: string; + + /** + * Specifies the Gaussian blur level. Accepts an integer value between 1 and 100, + * or an expression like `bl-10`. See + * [Blur](https://imagekit.io/docs/effects-and-enhancements#blur---bl). + */ + blur?: number; + + /** + * Adds a border to the output media. Accepts a string in the format + * `_` (e.g., `5_FFF000` for a 5px yellow border), or an + * expression like `ih_div_20_FF00FF`. See + * [Border](https://imagekit.io/docs/effects-and-enhancements#border---b). + */ + border?: string; + + /** + * Indicates whether the output image should retain the original color profile. See + * [Color profile](https://imagekit.io/docs/image-optimization#color-profile---cp). + */ + colorProfile?: boolean; + + /** + * Replaces colors in the image. Supports three formats: + * + * - `toColor` - Replace dominant color with the specified color. + * - `toColor_tolerance` - Replace dominant color with specified tolerance (0-100). + * - `toColor_tolerance_fromColor` - Replace a specific color with another within + * tolerance range. Colors can be hex codes (e.g., `FF0022`) or names (e.g., + * `red`, `blue`). See + * [Color replacement](https://imagekit.io/docs/effects-and-enhancements#color-replace---cr). + */ + colorReplace?: string; + + /** + * Automatically enhances the contrast of an image (contrast stretch). See + * [Contrast Stretch](https://imagekit.io/docs/effects-and-enhancements#contrast-stretch---e-contrast). + */ + contrastStretch?: true; + + /** + * Crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + */ + crop?: 'force' | 'at_max' | 'at_max_enlarge' | 'at_least' | 'maintain_ratio'; + + /** + * Additional crop modes for image resizing. See + * [Crop modes & focus](https://imagekit.io/docs/image-resize-and-crop#crop-crop-modes--focus). + */ + cropMode?: 'pad_resize' | 'extract' | 'pad_extract'; + + /** + * Specifies a fallback image if the resource is not found, e.g., a URL or file + * path. See + * [Default image](https://imagekit.io/docs/image-transformation#default-image---di). + */ + defaultImage?: string; + + /** + * Distorts the shape of an image. Supports two modes: + * + * - Perspective distortion: `p-x1_y1_x2_y2_x3_y3_x4_y4` changes the position of + * the four corners starting clockwise from top-left. + * - Arc distortion: `a-degrees` curves the image upwards (positive values) or + * downwards (negative values). See + * [Distort effect](https://imagekit.io/docs/effects-and-enhancements#distort---e-distort). + */ + distort?: string; + + /** + * Accepts values between 0.1 and 5, or `auto` for automatic device pixel ratio + * (DPR) calculation. Also accepts arithmetic expressions. + * + * - Learn about + * [Arithmetic expressions](https://imagekit.io/docs/arithmetic-expressions-in-transformations). + * - See [DPR](https://imagekit.io/docs/image-resize-and-crop#dpr---dpr). + */ + dpr?: number; + + /** + * Specifies the duration (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to indicate the length from the start offset. + * Arithmetic expressions are supported. See + * [Trim videos – Duration](https://imagekit.io/docs/trim-videos#duration---du). + */ + duration?: number | string; + + /** + * Specifies the end offset (in seconds) for trimming videos, e.g., `5` or `10.5`. + * Typically used with startOffset to define a time window. Arithmetic expressions + * are supported. See + * [Trim videos – End offset](https://imagekit.io/docs/trim-videos#end-offset---eo). + */ + endOffset?: number | string; + + /** + * Flips or mirrors an image either horizontally, vertically, or both. Acceptable + * values: `h` (horizontal), `v` (vertical), `h_v` (horizontal and vertical), or + * `v_h`. See [Flip](https://imagekit.io/docs/effects-and-enhancements#flip---fl). + */ + flip?: 'h' | 'v' | 'h_v' | 'v_h'; + + /** + * Refines padding and cropping behavior for pad resize, maintain ratio, and + * extract crop modes. Supports manual positions and coordinate-based focus. With + * AI-based cropping, you can automatically keep key subjects in frame—such as + * faces or detected objects (e.g., `fo-face`, `fo-person`, `fo-car`)— while + * resizing. + * + * - See [Focus](https://imagekit.io/docs/image-resize-and-crop#focus---fo). + * - [Object aware cropping](https://imagekit.io/docs/image-resize-and-crop#object-aware-cropping---fo-object-name) + */ + focus?: string; + + /** + * Specifies the output format for images or videos, e.g., `jpg`, `png`, `webp`, + * `mp4`, or `auto`. You can also pass `orig` for images to return the original + * format. ImageKit automatically delivers images and videos in the optimal format + * based on device support unless overridden by the dashboard settings or the + * format parameter. See + * [Image format](https://imagekit.io/docs/image-optimization#format---f) and + * [Video format](https://imagekit.io/docs/video-optimization#format---f). + */ + format?: 'auto' | 'webp' | 'jpg' | 'jpeg' | 'png' | 'gif' | 'svg' | 'mp4' | 'webm' | 'avif' | 'orig'; + + /** + * Creates a linear gradient with two colors. Pass `true` for a default gradient, + * or provide a string for a custom gradient. See + * [Gradient](https://imagekit.io/docs/effects-and-enhancements#gradient---e-gradient). + */ + gradient?: true | string; + + /** + * Enables a grayscale effect for images. See + * [Grayscale](https://imagekit.io/docs/effects-and-enhancements#grayscale---e-grayscale). + */ + grayscale?: true; + + /** + * Specifies the height of the output. If a value between 0 and 1 is provided, it + * is treated as a percentage (e.g., `0.5` represents 50% of the original height). + * You can also supply arithmetic expressions (e.g., `ih_mul_0.5`). Height + * transformation – + * [Images](https://imagekit.io/docs/image-resize-and-crop#height---h) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#height---h) + */ + height?: number | string; + + /** + * Specifies whether the output image (in JPEG or PNG) should be compressed + * losslessly. See + * [Lossless compression](https://imagekit.io/docs/image-optimization#lossless-webp-and-png---lo). + */ + lossless?: boolean; + + /** + * By default, ImageKit removes all metadata during automatic image compression. + * Set this to true to preserve metadata. See + * [Image metadata](https://imagekit.io/docs/image-optimization#image-metadata---md). + */ + metadata?: boolean; + + /** + * Named transformation reference. See + * [Named transformations](https://imagekit.io/docs/transformations#named-transformations). + */ + named?: string; + + /** + * Specifies the opacity level of the output image. See + * [Opacity](https://imagekit.io/docs/effects-and-enhancements#opacity---o). + */ + opacity?: number; + + /** + * If set to true, serves the original file without applying any transformations. + * See + * [Deliver original file as-is](https://imagekit.io/docs/core-delivery-features#deliver-original-file-as-is---orig-true). + */ + original?: boolean; + + /** + * Specifies an overlay to be applied on the parent image or video. ImageKit + * supports overlays including images, text, videos, subtitles, and solid colors. + * See + * [Overlay using layers](https://imagekit.io/docs/transformations#overlay-using-layers). + */ + overlay?: Overlay; + + /** + * Extracts a specific page or frame from multi-page or layered files (PDF, PSD, + * AI). For example, specify by number (e.g., `2`), a range (e.g., `3-4` for the + * 2nd and 3rd layers), or by name (e.g., `name-layer-4` for a PSD layer). See + * [Thumbnail extraction](https://imagekit.io/docs/vector-and-animated-images#get-thumbnail-from-psd-pdf-ai-eps-and-animated-files). + */ + page?: number | string; + + /** + * Specifies whether the output JPEG image should be rendered progressively. + * Progressive loading begins with a low-quality, pixelated version of the full + * image, which gradually improves to provide a faster perceived load time. See + * [Progressive images](https://imagekit.io/docs/image-optimization#progressive-image---pr). + */ + progressive?: boolean; + + /** + * Specifies the quality of the output image for lossy formats such as JPEG, WebP, + * and AVIF. A higher quality value results in a larger file size with better + * quality, while a lower value produces a smaller file size with reduced quality. + * See [Quality](https://imagekit.io/docs/image-optimization#quality---q). + */ + quality?: number; + + /** + * Specifies the corner radius for rounded corners. + * + * - Single value (positive integer): Applied to all corners (e.g., `20`). + * - `max`: Creates a circular or oval shape. + * - Per-corner array: Provide four underscore-separated values representing + * top-left, top-right, bottom-right, and bottom-left corners respectively (e.g., + * `10_20_30_40`). See + * [Radius](https://imagekit.io/docs/effects-and-enhancements#radius---r). + */ + radius?: number | 'max' | string; + + /** + * Pass any transformation not directly supported by the SDK. This transformation + * string is appended to the URL as provided. + */ + raw?: string; + + /** + * Specifies the rotation angle in degrees. Positive values rotate the image + * clockwise; you can also use, for example, `N40` for counterclockwise rotation or + * `auto` to use the orientation specified in the image's EXIF data. For videos, + * only the following values are supported: 0, 90, 180, 270, or 360. See + * [Rotate](https://imagekit.io/docs/effects-and-enhancements#rotate---rt). + */ + rotation?: number | string; + + /** + * Adds a shadow beneath solid objects in an image with a transparent background. + * For AI-based drop shadows, refer to aiDropShadow. Pass `true` for a default + * shadow, or provide a string for a custom shadow. See + * [Shadow](https://imagekit.io/docs/effects-and-enhancements#shadow---e-shadow). + */ + shadow?: true | string; + + /** + * Sharpens the input image, highlighting edges and finer details. Pass `true` for + * default sharpening, or provide a numeric value for custom sharpening. See + * [Sharpen](https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen). + */ + sharpen?: true | number; + + /** + * Specifies the start offset (in seconds) for trimming videos, e.g., `5` or + * `10.5`. Arithmetic expressions are also supported. See + * [Trim videos – Start offset](https://imagekit.io/docs/trim-videos#start-offset---so). + */ + startOffset?: number | string; + + /** + * An array of resolutions for adaptive bitrate streaming, e.g., [`240`, `360`, + * `480`, `720`, `1080`]. See + * [Adaptive Bitrate Streaming](https://imagekit.io/docs/adaptive-bitrate-streaming). + */ + streamingResolutions?: Array; + + /** + * Useful for images with a solid or nearly solid background and a central object. + * This parameter trims the background, leaving only the central object in the + * output image. See + * [Trim edges](https://imagekit.io/docs/effects-and-enhancements#trim-edges---t). + */ + trim?: true | number; + + /** + * Applies Unsharp Masking (USM), an image sharpening technique. Pass `true` for a + * default unsharp mask, or provide a string for a custom unsharp mask. See + * [Unsharp Mask](https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm). + */ + unsharpMask?: true | string; + + /** + * Specifies the video codec, e.g., `h264`, `vp9`, `av1`, or `none`. See + * [Video codec](https://imagekit.io/docs/video-optimization#video-codec---vc). + */ + videoCodec?: 'h264' | 'vp9' | 'av1' | 'none'; + + /** + * Specifies the width of the output. If a value between 0 and 1 is provided, it is + * treated as a percentage (e.g., `0.4` represents 40% of the original width). You + * can also supply arithmetic expressions (e.g., `iw_div_2`). Width transformation + * – [Images](https://imagekit.io/docs/image-resize-and-crop#width---w) · + * [Videos](https://imagekit.io/docs/video-resize-and-crop#width---w) + */ + width?: number | string; + + /** + * Focus using cropped image coordinates - X coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + x?: number | string; + + /** + * Focus using cropped image coordinates - X center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + xCenter?: number | string; + + /** + * Focus using cropped image coordinates - Y coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + y?: number | string; + + /** + * Focus using cropped image coordinates - Y center coordinate. See + * [Focus using cropped coordinates](https://imagekit.io/docs/image-resize-and-crop#example---focus-using-cropped-image-coordinates). + */ + yCenter?: number | string; + + /** + * Accepts a numeric value that determines how much to zoom in or out of the + * cropped area. It should be used in conjunction with fo-face or fo-. + * See [Zoom](https://imagekit.io/docs/image-resize-and-crop#zoom---z). + */ + zoom?: number; +} + +/** + * By default, the transformation string is added as a query parameter in the URL, + * e.g., `?tr=w-100,h-100`. If you want to add the transformation string in the + * path of the URL, set this to `path`. Learn more in the + * [Transformations guide](https://imagekit.io/docs/transformations). + */ +export type TransformationPosition = 'path' | 'query'; + +export interface VideoOverlay extends BaseOverlay { + /** + * Specifies the relative path to the video used as an overlay. + */ + input: string; + + type: 'video'; + + /** + * The input path can be included in the layer as either `i-{input}` or + * `ie-{base64_encoded_input}`. By default, the SDK determines the appropriate + * format automatically. To always use base64 encoding (`ie-{base64}`), set this + * parameter to `base64`. To always use plain text (`i-{input}`), set it to + * `plain`. + * + * Regardless of the encoding method: + * + * - Leading and trailing slashes are removed. + * - Remaining slashes within the path are replaced with `@@` when using plain + * text. + */ + encoding?: 'auto' | 'plain' | 'base64'; + + /** + * Array of transformation to be applied to the overlay video. Except + * `streamingResolutions`, all other video transformations are supported. See + * [Video transformations](https://imagekit.io/docs/video-transformation). + */ + transformation?: Array; +} diff --git a/src/responsive.ts b/src/responsive.ts index 3b3c120..73286ba 100644 --- a/src/responsive.ts +++ b/src/responsive.ts @@ -1,67 +1,10 @@ -import type { SrcOptions } from './interfaces' +import type { ResponsiveImageAttributes, GetImageAttributesOptions } from './interfaces' import { buildSrc } from './url' /* Default break‑point pools */ const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const -export interface GetImageAttributesOptions extends SrcOptions { - /** - * The intended display width of the image in pixels, - * used **only when the `sizes` attribute is not provided**. - * - * Triggers a DPR-based strategy (1x and 2x variants) and generates `x` descriptors in `srcSet`. - * - * Ignored if `sizes` is present. - */ - width?: number - - /** - * The value for the HTML `sizes` attribute - * (e.g., `"100vw"` or `"(min-width:768px) 50vw, 100vw"`). - * - * - If it includes one or more `vw` units, breakpoints smaller than the corresponding percentage of the smallest device width are excluded. - * - If it contains no `vw` units, the full breakpoint list is used. - * - * Enables a width-based strategy and generates `w` descriptors in `srcSet`. - */ - sizes?: string - - /** - * Custom list of **device-width breakpoints** in pixels. - * These define common screen widths for responsive image generation. - * - * Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. - * Sorted automatically. - */ - deviceBreakpoints?: number[] - - /** - * Custom list of **image-specific breakpoints** in pixels. - * Useful for generating small variants (e.g., placeholders or thumbnails). - * - * Merged with `deviceBreakpoints` before calculating `srcSet`. - * Defaults to `[16, 32, 48, 64, 96, 128, 256, 384]`. - * Sorted automatically. - */ - imageBreakpoints?: number[] -} - -/** - * Resulting set of attributes suitable for an HTML `` element. - * Useful for enabling responsive image loading. - */ -export interface ResponsiveImageAttributes { - /** URL for the *largest* candidate (assigned to plain `src`). */ - src: string - /** Candidate set with `w` or `x` descriptors. */ - srcSet?: string - /** `sizes` returned (or synthesised as `100vw`). */ - sizes?: string - /** Width as a number (if `width` was provided). */ - width?: number -} - /** * Generates a responsive image URL, `srcSet`, and `sizes` attributes * based on the input options such as `width`, `sizes`, and breakpoints. diff --git a/src/url.ts b/src/url.ts index 62a12c2..7de8031 100644 --- a/src/url.ts +++ b/src/url.ts @@ -1,5 +1,4 @@ -import type { SrcOptions } from "./interfaces"; -import type { ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces/Transformation"; +import type { SrcOptions, ImageOverlay, SolidColorOverlay, SubtitleOverlay, TextOverlay, Transformation, VideoOverlay } from "./interfaces"; import transformationUtils, { safeBtoa } from "./utils/transformation"; const TRANSFORMATION_PARAMETER = "tr"; const SIMPLE_OVERLAY_PATH_REGEX = new RegExp('^[a-zA-Z0-9-._/ ]*$') From a6a9a9353401bf490fadce314b77be0e07b2d7c9 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 16 Jan 2026 16:44:52 +0530 Subject: [PATCH 163/166] refactor: remove unused type import from responsive module in index.ts --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index dbbfb11..9ccff9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ import type { GetImageAttributesOptions, ResponsiveImageAttributes, SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; -import type { } from "./responsive"; import { getResponsiveImageAttributes } from "./responsive"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; From d6b72494e1c237a1bd7281072e89bc67de730437 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Fri, 16 Jan 2026 16:58:32 +0530 Subject: [PATCH 164/166] chore: update version to 5.2.0 and enhance changelog and README documentation --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 17 ++++++++++++++--- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b184c0f..bdf3053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## Version 5.2.0 + +1. **New transformation parameters added:** + - `layerMode` (`lm`): Control layer blending modes in overlay transformations + - `aiEdit` (`e-edit`): AI-powered image editing transformation + - `colorReplace` (`cr`): Replace specific colors in images + - `distort` (`e-distort`): Apply distortion effects to images + +2. **Type definitions updated:** + - Improved TypeScript types by synchronizing with the official Node.js SDK + - Enhanced type safety for transformation options, upload options, and responses + +3. **Documentation improvements:** + - Added TypeScript versioning policy section to README + - Clarified how type definition improvements may be released in minor/patch versions + +_No runtime breaking changes from 5.1.x._ + ## Version 5.1.0 1. **New helper** `getResponsiveImageAttributes()` diff --git a/README.md b/README.md index 2e5c86e..213d7d4 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,27 @@ You can install the SDK in your project using npm or yarn. npm install @imagekit/javascript ``` +## Documentation + +Refer to the ImageKit [official documentation](https://imagekit.io/docs/integration/javascript) for more details on how to use the SDK. + ## TypeScript support The SDK is written in TypeScript, offering first-class TypeScript support. Enjoy excellent type safety and IntelliSense in your IDE. You can use it in your TypeScript projects without any additional configuration. - To enable type checking in JavaScript projects, add `//@ts-check` at the top of your JavaScript files. This will activate type checking in your IDE. -## Documentation +### TypeScript and the SDK versioning policy -Refer to the ImageKit [official documentation](https://imagekit.io/docs/integration/javascript) for more details on how to use the SDK. +The TypeScript types in this SDK always reflect the latest shape of the ImageKit API. When we make improvements to the type definitions to better reflect the actual runtime behavior, we may release these changes in minor or patch versions. While these changes align types more closely with reality and are not breaking changes at runtime, they might cause new type errors when you upgrade. + +We judge this approach to be better than the alternatives: outdated, inaccurate types, or vastly more frequent major releases that would distract from any truly breaking runtime changes. If you encounter type errors after upgrading, you can resolve them by: + +- Adding appropriate type guards or assertions +- Updating your code to match the corrected types +- Using `// @ts-ignore` temporarily if you need more time to adjust + +Please feel welcome to share your thoughts about the versioning policy in a GitHub issue. ## Changelog diff --git a/package-lock.json b/package-lock.json index ffaffc7..c172246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.1.0", + "version": "5.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.1.0", + "version": "5.2.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 986fa4f..87dc486 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.1.0", + "version": "5.2.0", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js", From 584c9ba9afaa9d61d08c7e7b904ce90a0af3f437 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 29 Jan 2026 17:23:02 +0530 Subject: [PATCH 165/166] fix: update subtitle overlay key from 'l-subtitle' to 'l-subtitles' in URL generation --- src/url.ts | 2 +- test/url-generation/overlay.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/url.ts b/src/url.ts index 7de8031..e544a09 100644 --- a/src/url.ts +++ b/src/url.ts @@ -166,7 +166,7 @@ function processOverlay(overlay: Transformation["overlay"]): string | undefined } break; case "subtitle": - entries.push("l-subtitle"); + entries.push("l-subtitles"); { const subtitleOverlay = overlay as SubtitleOverlay; const enccoding = subtitleOverlay.encoding || "auto"; diff --git a/test/url-generation/overlay.js b/test/url-generation/overlay.js index b1edd4c..f99ae27 100644 --- a/test/url-generation/overlay.js +++ b/test/url-generation/overlay.js @@ -145,7 +145,7 @@ describe("Overlay Transformation Test Cases", function () { } }] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-subtitle,i-subtitle.srt,l-end/base-video.mp4`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-subtitles,i-subtitle.srt,l-end/base-video.mp4`); }); it("Solid color overlay generates correct URL with background color FF0000", function () { @@ -302,7 +302,7 @@ describe("Overlay Transformation Test Cases", function () { ] }); - expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitle,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`); + expect(url).equal(`https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-${encodeURIComponent("Every thing")},lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-${encodeURIComponent("Nested text overlay")},l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-subtitles,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end/base-image.jpg`); }); }); @@ -476,7 +476,7 @@ describe("Overlay encoding test cases", function () { } }] }); - expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitle,i-sub.srt,l-end/sample.mp4`); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitles,i-sub.srt,l-end/sample.mp4`); }); it('Subtitle overlay with explicit base64 encoding', function () { @@ -492,7 +492,7 @@ describe("Overlay encoding test cases", function () { } }] }); - expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitle,ie-${encodeURIComponent(safeBtoa("sub.srt"))},l-end/sample.mp4`); + expect(url).equal(`https://ik.imagekit.io/demo/tr:l-subtitles,ie-${encodeURIComponent(safeBtoa("sub.srt"))},l-end/sample.mp4`); }); it("Avoid double encoding when transformation string is in query params", function () { From 8d5abf5819fc307d50fc618446db33f8ccf04302 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Thu, 29 Jan 2026 17:23:39 +0530 Subject: [PATCH 166/166] chore: bump version to 5.2.1 and update changelog with subtitle overlay fix --- CHANGELOG.md | 5 +++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf3053..3025b8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 5.2.1 + +1. **Bug fix:** + - Updated subtitle overlay transformation parameter from `l-subtitle` to `l-subtitles` to align with API specification + ## Version 5.2.0 1. **New transformation parameters added:** diff --git a/package-lock.json b/package-lock.json index c172246..29c2372 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.2.0", + "version": "5.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.2.0", + "version": "5.2.1", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 87dc486..910b0f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.2.0", + "version": "5.2.1", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js",