From 1f028100d289fc88fefb5b752f9414abf645af69 Mon Sep 17 00:00:00 2001 From: Thomas Hilz Date: Thu, 23 Jul 2026 11:44:09 +0200 Subject: [PATCH] GPHDRUI-427 Upgrade axios from 0.x to 1.18.1 Bump axios from ^0.30.0 to ^1.18.1 (major upgrade) and review the codebase for breaking changes. Dependencies: - axios: ^0.30.0 -> ^1.18.1 - axios-mock-adapter: ^1.17.0 -> ^1.22.0 (axios 1.x compatibility) - update yarn.lock accordingly Breaking-change fixes: - ContactDocumentService.upload(): drop the manual "Content-Type: multipart/form-data" header. axios 1.x sets the multipart Content-Type incl. boundary automatically for FormData. However, the DomainRobotService constructor installs a global "application/json" default (axios.defaults.headers.common) that would otherwise win and break the multipart request, so clear the Content-Type per request (set to null) to let axios auto-detect it. This was caught by the new HTTP integration test - the mock-adapter suite could not surface it. Bug fixes in the previously unreachable ContactDocumentService: - fix broken module export (Contact -> ContactDocumentService) - remove reference to an undefined `axios` variable (obsolete after the Content-Type change) - wire the service into Domainrobot via a new contactDocument() factory so it is reachable through the public API Tests: - add unit/mock tests for ContactDocumentService (upload with/without keys, info, delete) - add real HTTP integration tests (no axios-mock-adapter) that validate axios 1.x behaviour the mock suite cannot reach: - ContactDocument upload: genuine multipart/form-data body with boundary - DomainRobotService: basic auth + context header on the wire, JSON response parsing, response headers via AxiosHeaders, 4xx -> DomainRobotException mapping, connection errors without a response - DomainService: PUT with verb-suffixed path + JSON body, GET with dotted domain name, keys[] query string kept intact on the wire (params serialization change does not affect this SDK), body-less POST - add "test:integration" npm script (runs tests/Integration/** separately from the mock suite so the global mock adapter does not intercept real requests) Unaffected by the upgrade (verified): basic auth config, global default headers, request/response flow, error handling via error.response, and parameter serialization (URLs are built manually, `params` is not used). --- package.json | 7 +- src/Domainrobot.js | 6 + src/services/ContactDocumentService.js | 15 +- .../ContactDocumentUpload.integration.test.js | 89 +++++++++ .../DomainRobotService.integration.test.js | 184 ++++++++++++++++++ .../DomainService.integration.test.js | 133 +++++++++++++ .../services/ContactDocumentService.test.js | 121 ++++++++++++ yarn.lock | 75 ++++--- 8 files changed, 601 insertions(+), 29 deletions(-) create mode 100644 tests/Integration/ContactDocumentUpload.integration.test.js create mode 100644 tests/Integration/DomainRobotService.integration.test.js create mode 100644 tests/Integration/DomainService.integration.test.js create mode 100644 tests/Specs/services/ContactDocumentService.test.js diff --git a/package.json b/package.json index 06a2df5..1e613d0 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "update:pcdomainsapi": "cp node_modules/@internetx/internetx-swagger-files/src/pcdomains.json src/swagger", "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs", - "test:mocha": "nyc mocha \"tests/Specs/**/*.js\"" + "test:mocha": "nyc mocha \"tests/Specs/**/*.js\"", + "test:integration": "nyc mocha \"tests/Integration/**/*.js\"" }, "nyc": { "exclude": [ @@ -38,14 +39,14 @@ }, "devDependencies": { "@internetx/internetx-swagger-files": "https://github.com/InterNetX/domainrobot-api.git", - "axios-mock-adapter": "^1.17.0", + "axios-mock-adapter": "^1.22.0", "cross-env": "^6.0.3", "mocha": "^9.2.2", "nyc": "^15.1.0", "webpack": "^5.99.5" }, "dependencies": { - "axios": "^0.30.0", + "axios": "^1.18.1", "chai": "^4.3.4", "yarn.lock": "^0.0.1-security" }, diff --git a/src/Domainrobot.js b/src/Domainrobot.js index 2cf01b4..8971822 100644 --- a/src/Domainrobot.js +++ b/src/Domainrobot.js @@ -2,6 +2,7 @@ // services const ContactService = require("./services/ContactService") +const ContactDocumentService = require("./services/ContactDocumentService") const CertificateService = require("./services/CertificateService") const DomainStudio = require("./services/DomainStudio") const DomainService = require("./services/DomainService") @@ -33,6 +34,11 @@ class DomainRobot { return new ContactService(this.domainRobotConfig) } + // contact document stuff + contactDocument() { + return new ContactDocumentService(this.domainRobotConfig) + } + // certificate stuff certificate() { return new CertificateService(this.domainRobotConfig) diff --git a/src/services/ContactDocumentService.js b/src/services/ContactDocumentService.js index 23d7ba0..0b1ff5b 100644 --- a/src/services/ContactDocumentService.js +++ b/src/services/ContactDocumentService.js @@ -10,9 +10,16 @@ class ContactDocumentService extends DomainRobotService { if (keys.length > 0) { keysString = "?keys[]=" + keys.join("&keys[]="); } - axios.defaults.headers.common['Content-Type'] = - "multipart/form-data"; - + // The base service sets a global default Content-Type of + // "application/json" (axios.defaults.headers.common). For a FormData + // upload that default would win and break the multipart request, so we + // clear it per-request. axios 1.x then auto-detects the FormData + // payload and sets "multipart/form-data" incl. the boundary itself. + this.axiosconfig.headers = { + ...(this.axiosconfig.headers || {}), + "Content-Type": null, + }; + return await this.sendPostRequest( this.domainRobotConfig.url + "/contact/" + contactId + "/document/" + type + keysString, formData @@ -77,4 +84,4 @@ class ContactDocumentService extends DomainRobotService { } } -module.exports = Contact; +module.exports = ContactDocumentService; diff --git a/tests/Integration/ContactDocumentUpload.integration.test.js b/tests/Integration/ContactDocumentUpload.integration.test.js new file mode 100644 index 0000000..c92710d --- /dev/null +++ b/tests/Integration/ContactDocumentUpload.integration.test.js @@ -0,0 +1,89 @@ +/* global describe, it, beforeEach, afterEach, require */ + +// Real HTTP integration test (NO axios-mock-adapter). +// It spins up a local HTTP server so the request actually travels through +// axios' real adapter. This is the only way to verify axios 1.x behaviour +// that a mock-adapter based test cannot reach: that a FormData payload +// produces a genuine `multipart/form-data` body incl. boundary. + +const http = require("http"); +const Domainrobot = require("../../src/Domainrobot"); +const expect = require("chai").expect; + +describe("ContactDocumentService upload (integration)", () => { + let server; + let port; + let lastRequest; + + beforeEach((done) => { + lastRequest = null; + server = http.createServer((req, res) => { + const chunks = []; + req.on("data", (c) => chunks.push(c)); + req.on("end", () => { + lastRequest = { + method: req.method, + url: req.url, + headers: req.headers, + body: Buffer.concat(chunks).toString("utf8"), + }; + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + stid: "integration-test", + status: { code: "S0000", type: "SUCCESS" }, + }) + ); + }); + }); + server.listen(0, "127.0.0.1", () => { + port = server.address().port; + done(); + }); + }); + + afterEach((done) => { + server.close(done); + }); + + it("sends a genuine multipart/form-data body with boundary", async () => { + const baseUrl = `http://127.0.0.1:${port}`; + + const formData = new FormData(); + formData.append( + "file", + new Blob(["dummy-id-document-content"]), + "idcard.pdf" + ); + + const domainRobot = new Domainrobot({ + url: baseUrl, + auth: { user: "user", password: "password", context: "9" }, + }); + + const result = await domainRobot + .contactDocument() + .upload(1, "idcard", formData); + + expect(result.status).to.be.equal(200); + + expect(lastRequest, "server received a request").to.not.be.equal(null); + expect(lastRequest.method).to.be.equal("POST"); + expect(lastRequest.url).to.be.equal("/contact/1/document/idcard"); + + // The key axios 1.x assertion: the wire Content-Type must be + // multipart/form-data WITH a boundary - not the global application/json + // default set in the DomainRobotService constructor. + const contentType = lastRequest.headers["content-type"]; + expect(contentType, "Content-Type header").to.match( + /^multipart\/form-data; boundary=/ + ); + + // The boundary from the header must actually delimit the body, i.e. + // axios produced a real multipart body from the FormData payload. + const boundary = contentType.split("boundary=")[1]; + expect(lastRequest.body).to.contain(boundary); + expect(lastRequest.body).to.contain("idcard.pdf"); + expect(lastRequest.body).to.contain("dummy-id-document-content"); + }); +}); diff --git a/tests/Integration/DomainRobotService.integration.test.js b/tests/Integration/DomainRobotService.integration.test.js new file mode 100644 index 0000000..39d3d44 --- /dev/null +++ b/tests/Integration/DomainRobotService.integration.test.js @@ -0,0 +1,184 @@ +/* global describe, it, beforeEach, afterEach, require, Buffer */ + +// Real HTTP integration tests (NO axios-mock-adapter). +// These exercise axios 1.x behaviour that a mock-adapter based suite cannot +// reach, because the mock intercepts BELOW axios' real serialization/parsing: +// - strict JSON parsing of the response body +// - response headers exposed as an AxiosHeaders object +// - error handling: a non-2xx response throws and is mapped to +// DomainRobotException (axios 1.x consistently throws) +// - connection errors without a response are rethrown untouched +// - Basic auth + custom headers are actually put on the wire +// - JSON request bodies are serialized with application/json +// +// A local HTTP server captures the incoming request and replies with a +// per-test configurable responder. + +const http = require("http"); +const Domainrobot = require("../../src/Domainrobot"); +const DomainRobotException = require("../../src/lib/DomainRobotException"); +const ApiFactory = require("../../src/lib/Factory"); +const domainrobot = require("../../src/swagger/domainrobot.json"); +const expect = require("chai").expect; + +const Backend = new ApiFactory(domainrobot); +const DomainRobotModels = Backend.models; + +describe("DomainRobotService HTTP (integration)", () => { + let server; + let port; + let lastRequest; + let responder; + + beforeEach((done) => { + lastRequest = null; + + // default: 200 + a JSON body and a custom response header + responder = (req, res) => { + res.writeHead(200, { + "Content-Type": "application/json", + "X-Domainrobot-Stid": "stid-integration-123", + }); + res.end( + JSON.stringify({ + stid: "stid-integration-123", + status: { code: "S0000", type: "SUCCESS" }, + }) + ); + }; + + server = http.createServer((req, res) => { + const chunks = []; + req.on("data", (c) => chunks.push(c)); + req.on("end", () => { + lastRequest = { + method: req.method, + url: req.url, + headers: req.headers, + body: Buffer.concat(chunks).toString("utf8"), + }; + responder(req, res); + }); + }); + server.listen(0, "127.0.0.1", () => { + port = server.address().port; + done(); + }); + }); + + afterEach((done) => { + if (server) { + server.close(done); + } else { + done(); + } + }); + + function makeClient() { + return new Domainrobot({ + url: `http://127.0.0.1:${port}`, + auth: { user: "theuser", password: "thepass", context: "9" }, + }); + } + + it("puts basic auth + context header on the wire and parses the JSON response", async () => { + const result = await makeClient().contact().info(42); + + // --- wire --- + expect(lastRequest.method).to.be.equal("GET"); + expect(lastRequest.url).to.be.equal("/contact/42"); + + const expectedAuth = + "Basic " + Buffer.from("theuser:thepass").toString("base64"); + expect(lastRequest.headers["authorization"]).to.be.equal(expectedAuth); + expect(lastRequest.headers["x-domainrobot-context"]).to.be.equal("9"); + + // --- response: real JSON parsing by axios 1.x --- + expect(result.status).to.be.equal(200); + expect(result.result).to.be.an("object"); + expect(result.result.status.type).to.be.equal("SUCCESS"); + }); + + it("exposes response headers (AxiosHeaders) via getHeaders()", async () => { + const result = await makeClient().contact().info(1); + + const headers = result.getHeaders(); + expect(headers).to.not.be.equal(undefined); + // axios 1.x normalizes response header names to lower case + expect(headers["x-domainrobot-stid"]).to.be.equal( + "stid-integration-123" + ); + }); + + it("serializes a model as an application/json request body", async () => { + const contact = new DomainRobotModels.Contact({ + city: "Regensburg", + country: "DE", + }); + + const result = await makeClient().contact().create(contact); + + expect(lastRequest.method).to.be.equal("POST"); + expect(lastRequest.url).to.be.equal("/contact"); + expect(lastRequest.headers["content-type"]).to.match( + /^application\/json/ + ); + + // The body must be valid JSON (axios 1.x serialized it) ... + let parsed; + expect(() => { + parsed = JSON.parse(lastRequest.body); + }).to.not.throw(); + expect(parsed).to.be.an("object"); + expect(lastRequest.body).to.contain("Regensburg"); + + expect(result.status).to.be.equal(200); + }); + + it("throws DomainRobotException with status + body on a 4xx response", async () => { + responder = (req, res) => { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: { code: "EF02324", type: "ERROR" }, + messages: ["Validation failed"], + }) + ); + }; + + let thrown; + try { + await makeClient().contact().info(1); + } catch (e) { + thrown = e; + } + + expect(thrown, "an exception was thrown").to.not.be.equal(undefined); + expect(thrown.type).to.be.equal("DomainRobotException"); + expect(thrown).to.be.an.instanceof(DomainRobotException); + expect(thrown.status).to.be.equal(400); + // the response body is mapped onto exception.error + expect(thrown.error.status.type).to.be.equal("ERROR"); + }); + + it("rethrows connection errors that carry no response", async () => { + const client = makeClient(); + + // Close the server so the connection is refused (no HTTP response at all). + await new Promise((resolve) => server.close(resolve)); + server = null; // afterEach guard: already closed + + let thrown; + try { + await client.contact().info(1); + } catch (e) { + thrown = e; + } + + expect(thrown, "an error was thrown").to.not.be.equal(undefined); + // took the `error.response === undefined` branch -> raw error, not a + // DomainRobotException + expect(thrown.response).to.be.equal(undefined); + expect(thrown.type).to.not.be.equal("DomainRobotException"); + }); +}); diff --git a/tests/Integration/DomainService.integration.test.js b/tests/Integration/DomainService.integration.test.js new file mode 100644 index 0000000..a26daf3 --- /dev/null +++ b/tests/Integration/DomainService.integration.test.js @@ -0,0 +1,133 @@ +/* global describe, it, beforeEach, afterEach, require, Buffer */ + +// Real HTTP integration tests for DomainService (NO axios-mock-adapter). +// Complements DomainRobotService.integration.test.js by exercising the axios +// 1.x request/response flow through a second, more varied service: +// - PUT with a verb-suffixed path (_renew) + JSON body +// - GET with a dotted domain name in the path +// - POST _search whose manually built `?keys[]=...` query string must reach +// the wire intact (proves the axios 1.x params-serialization change does +// NOT affect this SDK, which builds URLs itself instead of using `params`) +// - POST with no request body +// +// A local HTTP server captures the incoming request and replies 200 + JSON. + +const http = require("http"); +const Domainrobot = require("../../src/Domainrobot"); +const ApiFactory = require("../../src/lib/Factory"); +const domainrobot = require("../../src/swagger/domainrobot.json"); +const expect = require("chai").expect; + +const Backend = new ApiFactory(domainrobot); +const DomainRobotModels = Backend.models; + +describe("DomainService HTTP (integration)", () => { + let server; + let port; + let lastRequest; + + beforeEach((done) => { + lastRequest = null; + server = http.createServer((req, res) => { + const chunks = []; + req.on("data", (c) => chunks.push(c)); + req.on("end", () => { + lastRequest = { + method: req.method, + url: req.url, + headers: req.headers, + body: Buffer.concat(chunks).toString("utf8"), + }; + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + stid: "domain-integration", + status: { code: "S0000", type: "SUCCESS" }, + }) + ); + }); + }); + server.listen(0, "127.0.0.1", () => { + port = server.address().port; + done(); + }); + }); + + afterEach((done) => { + server.close(done); + }); + + function makeClient() { + return new Domainrobot({ + url: `http://127.0.0.1:${port}`, + auth: { user: "theuser", password: "thepass", context: "9" }, + }); + } + + it("sends a PUT renew with a verb-suffixed path and JSON body", async () => { + const domain = new DomainRobotModels.Domain({ name: "example.com" }); + + const result = await makeClient().domain().renew(domain); + + expect(lastRequest.method).to.be.equal("PUT"); + expect(lastRequest.url).to.be.equal("/domain/example.com/_renew"); + expect(lastRequest.headers["content-type"]).to.match( + /^application\/json/ + ); + + let parsed; + expect(() => { + parsed = JSON.parse(lastRequest.body); + }).to.not.throw(); + expect(parsed.name).to.be.equal("example.com"); + + expect(result.status).to.be.equal(200); + }); + + it("sends a GET info with a dotted domain name in the path", async () => { + const result = await makeClient().domain().info("example.com"); + + expect(lastRequest.method).to.be.equal("GET"); + expect(lastRequest.url).to.be.equal("/domain/example.com"); + expect(result.status).to.be.equal(200); + expect(result.result.status.type).to.be.equal("SUCCESS"); + }); + + it("keeps the manually built keys[] query string intact on the wire", async () => { + const query = new DomainRobotModels.Query({ + filters: [ + { key: "tld", value: "com", operator: "EQUAL" }, + ], + }); + const keys = ["tld", "status"]; + + const result = await makeClient().domain().list(query, keys); + + expect(lastRequest.method).to.be.equal("POST"); + + // encodeURI leaves [ ] ? & = untouched; the bracket notation must not + // be mangled by axios' params serialization (which is not used here). + const url = decodeURIComponent(lastRequest.url); + expect(url).to.contain("/domain/_search?"); + expect(url).to.contain("keys[]=tld"); + expect(url).to.contain("keys[]=status"); + + expect(result.status).to.be.equal(200); + }); + + it("sends a POST with no request body (authInfo1Create)", async () => { + const result = await makeClient() + .domain() + .authInfo1Create("example.com"); + + expect(lastRequest.method).to.be.equal("POST"); + expect(lastRequest.url).to.be.equal("/domain/example.com/_authinfo1"); + // No payload is passed, so sendRequest falls back to its `data = null` + // default. With the global application/json Content-Type axios + // serializes that to the JSON literal "null" (same behaviour in 0.x and + // 1.x - not an upgrade regression). + expect(lastRequest.body).to.be.equal("null"); + + expect(result.status).to.be.equal(200); + }); +}); diff --git a/tests/Specs/services/ContactDocumentService.test.js b/tests/Specs/services/ContactDocumentService.test.js new file mode 100644 index 0000000..858640e --- /dev/null +++ b/tests/Specs/services/ContactDocumentService.test.js @@ -0,0 +1,121 @@ +/* global describe, it, beforeEach, expect, require */ + +const Domainrobot = require("../../../src/Domainrobot"); +const ValidResponse = require("../../mock/ValidResponse.json"); +const expect = require('chai').expect; +const axiosMock = require('../../axios-mock'); + +describe("ContactDocumentServiceTest", () => { + let domainRobot; + + beforeEach(function () { + domainRobot = new Domainrobot({ + url: "http://dev-proxy-lab.intern.autodns-lab.com:10025", + auth: { + user: "user", + password: "password", + context: "9" + } + }); + }); + + it("upload", async () => { + const formData = new FormData(); + formData.append("file", new Blob(["dummy"]), "idcard.pdf"); + + axiosMock().onPost().reply(200, ValidResponse); + + let capturedHeaders; + let result; + + try { + result = await domainRobot + .contactDocument() + .logRequest(function (requestOptions, headers) { + expect(requestOptions.method).to.be.equal('POST'); + expect(requestOptions.url).to.match(/.+\/contact\/1\/document\/idcard$/); + expect(requestOptions.data).to.be.equal(formData); + capturedHeaders = headers; + }) + .logResponse(function (response, executionTime) { + expect(executionTime).to.be.a('number'); + expect(response).to.be.a('object'); + }) + .upload(1, "idcard", formData); + } catch (DomainRobotException) { + console.log(DomainRobotException); + } + + // The manual "multipart/form-data" default header must no longer be + // forced: axios 1.x sets Content-Type (incl. boundary) automatically + // for FormData payloads. + expect(capturedHeaders.common['Content-Type']).to.not.equal('multipart/form-data'); + + expect(result).to.be.a("object"); + expect(result.status).to.be.equal(200); + }); + + it("upload with keys", async () => { + const formData = new FormData(); + formData.append("file", new Blob(["dummy"]), "idcard.pdf"); + const keys = ['force']; + + axiosMock().onPost().reply(200, ValidResponse); + + let result; + + try { + result = await domainRobot + .contactDocument() + .logRequest(function (requestOptions, headers) { + expect(requestOptions.method).to.be.equal('POST'); + expect(requestOptions.url).to.match(/.+\/contact\/1\/document\/idcard\?keys.+=force$/); + }) + .upload(1, "idcard", formData, keys); + } catch (DomainRobotException) { + console.log(DomainRobotException); + } + expect(result).to.be.a("object"); + expect(result.status).to.be.equal(200); + }); + + it("info", async () => { + axiosMock().onGet().reply(200, ValidResponse); + + let result; + + try { + result = await domainRobot + .contactDocument() + .logRequest(function (requestOptions, headers) { + expect(requestOptions.method).to.be.equal('GET'); + expect(requestOptions.url).to.match(/.+\/contact\/1$/); + }) + .info(1); + } catch (DomainRobotException) { + console.log(DomainRobotException); + } + expect(result).to.be.a("object"); + expect(result.status).to.be.equal(200); + }); + + it("delete", async () => { + axiosMock().onDelete().reply(200, ValidResponse); + + let result; + + try { + result = await domainRobot + .contactDocument() + .logRequest(function (requestOptions, headers) { + expect(requestOptions.method).to.be.equal('DELETE'); + expect(requestOptions.url).to.match(/.+\/contact\/1$/); + }) + .delete(1); + } catch (DomainRobotException) { + console.log(DomainRobotException); + } + expect(result).to.be.a("object"); + expect(result.status).to.be.equal(200); + }); +}); diff --git a/yarn.lock b/yarn.lock index dd98be5..9f55592 100644 --- a/yarn.lock +++ b/yarn.lock @@ -378,6 +378,13 @@ acorn@^8.14.0, acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -469,7 +476,7 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios-mock-adapter@^1.17.0: +axios-mock-adapter@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz#0f3e6be0fc9b55baab06f2d49c0b71157e7c053d" integrity sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw== @@ -477,14 +484,15 @@ axios-mock-adapter@^1.17.0: fast-deep-equal "^3.1.3" is-buffer "^2.0.5" -axios@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.30.0.tgz#026ae2c0ae6ac35d564056690683fb77c991d0d3" - integrity sha512-Z4F3LjCgfjZz8BMYalWdMgAQUnEtKDmpwNHjh/C8pQZWde32TF64cqnSeyL3xD/aTIASRU30RHTNzRiV/NpGMg== +axios@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.18.1.tgz#d63f9863bcd8938815c86f9e2abd380189d96dfe" + integrity sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g== dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + follow-redirects "^1.16.0" + form-data "^4.0.5" + https-proxy-agent "^5.0.1" + proxy-from-env "^2.1.0" balanced-match@^1.0.0: version "1.0.2" @@ -695,6 +703,13 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +debug@4: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + debug@4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -899,10 +914,10 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -follow-redirects@^1.15.4: - version "1.15.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" - integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== +follow-redirects@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== foreground-child@^2.0.0: version "2.0.0" @@ -912,15 +927,16 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" -form-data@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" - integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== +form-data@^4.0.5: + version "4.0.6" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.6.tgz#28e864e1b786dbebb68db1f452f9635278665827" + integrity sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" es-set-tostringtag "^2.1.0" - mime-types "^2.1.12" + hasown "^2.0.4" + mime-types "^2.1.35" fromentries@^1.2.0: version "1.3.2" @@ -1074,6 +1090,13 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hasown@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" + integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -1084,6 +1107,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -1355,7 +1386,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27: +mime-types@^2.1.27, mime-types@^2.1.35: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -1577,10 +1608,10 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" + integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== randombytes@^2.1.0: version "2.1.0"