From f485cd7d7b69adb87297b974dfe093287f23665a Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 22 Mar 2023 14:59:49 +0100 Subject: [PATCH 001/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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="https://github.com/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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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="https://github.com/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/160] 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/160] 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(/service/urloptions: UrlOptions): string { - return url(https://github.com/urlOptions,%20this.options); + url(/service/urloptions: UrlOptions & Partial>): string { + // Merge the options with the instance options + const options = { + ...this.options, + ...urlOptions, + }; + return ImageKit.url(https://github.com/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(/service/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/160] 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(/service/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(https://github.com/%7B%20src:%20%22/%22%20%7D); - 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/160] 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(https://github.com/%7B%7D); 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/160] 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(/service/urloptions: UrlOptions & Partial>): string { - // Merge the options with the instance options - const options = { - ...this.options, - ...urlOptions, - }; - return ImageKit.url(https://github.com/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(/service/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(https://github.com/%7B%7D); + 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(https://github.com/%7B%20src:%20%22/%22%20%7D); + 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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="https://github.com/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(/service/urloptions: UrlOptions): string { +- return url(https://github.com/urlOptions,%20this.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(https://github.com/%7B%7D); ++ 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(https://github.com/%7B%20src:%20%22/%22%20%7D); ++ 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/160] 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="https://github.com/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(/service/urloptions: UrlOptions): string { -- return url(https://github.com/urlOptions,%20this.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(https://github.com/%7B%7D); -+ 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(https://github.com/%7B%20src:%20%22/%22%20%7D); -+ 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/160] 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/160] 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/160] 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/160] 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%202x", + 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%20200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max%20400w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max%20800w", + 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max%202x", + 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max%202x", + 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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/160] 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", - 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", - 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%202x", - 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%202x", 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%20200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max%20400w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max%20800w", - 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max%202x", - 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-1080,c-at_max%202x", 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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%20384w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max%202x", - 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%201x,%20https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-1080,c-at_max%202x", 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%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", - sizes: "(min-width: 800px) 33vw, 100vw", - width: undefined + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max%20256w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max%20384w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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/160] 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%201x,%20https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-1080,c-at_max/sample.jpg?key=value%202x", + 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/160] 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/160] 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%2016w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-32,c-at_max%2032w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-48,c-at_max%2048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-64,c-at_max%2064w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-96,c-at_max%2096w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-128,c-at_max%20128w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max%20256w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max%20384w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max%20640w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max%20750w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max%20828w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max%201080w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max%201200w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max%201920w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max%202048w,%20https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max%203840w", + 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/160] 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/160] 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/160] 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/160] 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/160] 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/160] 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",