From 5f96cda5e211eb23da09ccc8758c5c4e903208ad Mon Sep 17 00:00:00 2001 From: James George <25279263+jamesgeorge007@users.noreply.github.com> Date: Fri, 26 Jul 2024 10:26:58 -0700 Subject: [PATCH] feat(cli): add support for JUnit reporter (#4189) --- packages/hoppscotch-cli/package.json | 9 +- .../commands/__snapshots__/test.spec.ts.snap | 529 ++++++++++++++++++ .../src/__tests__/e2e/commands/test.spec.ts | 165 +++++- .../test-junit-report-export-coll.json | 150 +++++ packages/hoppscotch-cli/src/commands/test.ts | 4 +- packages/hoppscotch-cli/src/handlers/error.ts | 4 + packages/hoppscotch-cli/src/index.ts | 17 +- packages/hoppscotch-cli/src/types/commands.ts | 1 + packages/hoppscotch-cli/src/types/errors.ts | 1 + .../hoppscotch-cli/src/utils/collections.ts | 119 ++-- .../hoppscotch-cli/src/utils/pre-request.ts | 5 +- .../src/utils/reporters/junit.ts | 178 ++++++ packages/hoppscotch-cli/src/utils/request.ts | 13 +- packages/hoppscotch-cli/tsconfig.json | 3 +- pnpm-lock.yaml | 69 ++- 15 files changed, 1200 insertions(+), 67 deletions(-) create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap create mode 100644 packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/test-junit-report-export-coll.json create mode 100644 packages/hoppscotch-cli/src/utils/reporters/junit.ts diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 45195e97e..134e9fe6b 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/cli", - "version": "0.9.1", + "version": "0.10.0", "description": "A CLI to run Hoppscotch test scripts in CI environments.", "homepage": "https://hoppscotch.io", "type": "module", @@ -20,9 +20,11 @@ "debugger": "node debugger.js 9999", "prepublish": "pnpm exec tsup", "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write", - "test": "pnpm run build && vitest run && rm -rf dist", + "pretest": "pnpm run build", + "test": "vitest run", "do-typecheck": "pnpm exec tsc --noEmit", - "do-test": "pnpm test" + "do-test": "pnpm run test", + "postinstall": "pnpm run build" }, "keywords": [ "cli", @@ -48,6 +50,7 @@ "lodash-es": "4.17.21", "qs": "6.11.2", "verzod": "0.2.2", + "xmlbuilder2": "3.1.1", "zod": "3.22.4" }, "devDependencies": { diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap new file mode 100644 index 000000000..055c9a861 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap @@ -0,0 +1,529 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the default path 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >\\" +} (ENV_EXPAND_LOOP)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the specified path 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >\\" +} (ENV_EXPAND_LOOP)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection referring to environment variables 1`] = ` +" + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection with authorization/headers set at the collection level 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + { - return !updatedFolder.headers.some((folderHeaderEntries) => folderHeaderEntries.key === collectionHeaderEntries.key) - }) - updatedFolder.headers.push(...filteredHeaders); - } - - collectionStack.push({ - path: `${path}/${updatedFolder.name}`, - collection: updatedFolder, - }); - } + // Storing current request's report. + const requestReport = result.report; + requestsReport.push(requestReport); } + // Pushing remaining folders realted collection to stack. + for (const folder of collection.folders) { + const updatedFolder: HoppCollection = { ...folder }; + + if (updatedFolder.auth?.authType === "inherit") { + updatedFolder.auth = collection.auth; + } + + if (collection.headers?.length) { + // Filter out header entries present in the parent collection under the same name + // This ensures the folder headers take precedence over the collection headers + const filteredHeaders = collection.headers.filter( + (collectionHeaderEntries) => { + return !updatedFolder.headers.some( + (folderHeaderEntries) => + folderHeaderEntries.key === collectionHeaderEntries.key + ); + } + ); + updatedFolder.headers.push(...filteredHeaders); + } + + collectionStack.push({ + path: `${path}/${updatedFolder.name}`, + collection: updatedFolder, + }); + } + } + return requestsReport; }; @@ -134,7 +143,8 @@ const getCollectionStack = (collections: HoppCollection[]): CollectionStack[] => * False, if errors occurred or test-cases failed. */ export const collectionsRunnerResult = ( - requestsReport: RequestReport[] + requestsReport: RequestReport[], + reporterJUnitExportPath?: string ): boolean => { const overallTestMetrics = { tests: { failed: 0, passed: 0 }, @@ -152,6 +162,9 @@ export const collectionsRunnerResult = ( }; let finalResult = true; + let totalErroredTestCases = 0; + let totalFailedTestCases = 0; + // Printing requests-report details of failed-tests and errors for (const requestReport of requestsReport) { const { path, tests, errors, result, duration } = requestReport; @@ -165,6 +178,19 @@ export const collectionsRunnerResult = ( printErrorsReport(path, errors); + if (reporterJUnitExportPath) { + const { failedRequestTestCases, erroredRequestTestCases } = + buildJUnitReport({ + path, + tests, + errors, + duration: duration.test, + }); + + totalFailedTestCases += failedRequestTestCases; + totalErroredTestCases += erroredRequestTestCases; + } + /** * Extracting current request report's test-metrics and updating * overall test-metrics. @@ -216,6 +242,19 @@ export const collectionsRunnerResult = ( printRequestsMetrics(overallRequestMetrics); printPreRequestMetrics(overallPreRequestMetrics); + if (reporterJUnitExportPath) { + const totalTestCases = + overallTestMetrics.tests.failed + overallTestMetrics.tests.passed; + + generateJUnitReportExport({ + totalTestCases, + totalFailedTestCases, + totalErroredTestCases, + testDuration: overallTestMetrics.duration, + reporterJUnitExportPath, + }); + } + return finalResult; }; diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index b1a760ab3..f39410644 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -47,7 +47,10 @@ export const preRequestScriptRunner = ( ), TE.map( ({ selected, global }) => - { name: "Env", variables: [...selected, ...global] } + { + name: "Env", + variables: [...(selected ?? []), ...(global ?? [])], + } ), TE.chainEitherKW((env) => getEffectiveRESTRequest(request, env)), TE.mapLeft((reason) => diff --git a/packages/hoppscotch-cli/src/utils/reporters/junit.ts b/packages/hoppscotch-cli/src/utils/reporters/junit.ts new file mode 100644 index 000000000..a47fdf546 --- /dev/null +++ b/packages/hoppscotch-cli/src/utils/reporters/junit.ts @@ -0,0 +1,178 @@ +import { info, log } from "console"; +import fs from "fs"; +import path from "path"; + +import { create } from "xmlbuilder2"; +import { XMLBuilder } from "xmlbuilder2/lib/interfaces"; +import { TestReport } from "../../interfaces/response"; +import { error, HoppCLIError } from "../../types/errors"; +import { RequestReport } from "../../types/request"; +import { exceptionColors } from "../getters"; + +type BuildJUnitReportArgs = Omit & { + duration: RequestReport["duration"]["test"]; +}; + +type BuildJUnitReportResult = { + failedRequestTestCases: number; + erroredRequestTestCases: number; +}; + +type GenerateJUnitReportExportArgs = { + totalTestCases: number; + totalFailedTestCases: number; + totalErroredTestCases: number; + testDuration: number; + reporterJUnitExportPath: string; +}; + +const { INFO, SUCCESS } = exceptionColors; + +// Create the root XML element +const rootEl = create({ version: "1.0", encoding: "UTF-8" }).ele("testsuites"); + +/** + * Builds a JUnit report based on the provided request report. + * Creates a test suite at the request level populating the XML document structure. + * + * @param {BuildJUnitReportArgs} options - The options to build the JUnit report. + * @param {string} options.path - The path of the request. + * @param {TestReport[]} options.tests - The test suites for the request. + * @param {HoppCLIError[]} options.errors - The errors encountered during the request. + * @param {number} options.duration - Time taken to execute the test suite. + * @returns {BuildJUnitReportResult} An object containing the number of failed and errored test cases. + */ +export const buildJUnitReport = ({ + path, + tests: testSuites, + errors: requestTestSuiteErrors, + duration: testSuiteDuration, +}: BuildJUnitReportArgs): BuildJUnitReportResult => { + let requestTestSuiteError: XMLBuilder | null = null; + + // Create a test suite at the request level + const requestTestSuite = rootEl.ele("testsuite", { + name: path, + time: testSuiteDuration, + timestamp: new Date().toISOString(), + }); + + if (requestTestSuiteErrors.length > 0) { + requestTestSuiteError = requestTestSuite.ele("system-err"); + } + + let systemErrContent = ""; + + requestTestSuiteErrors.forEach((error) => { + let compiledError = error.code; + + if ("data" in error) { + compiledError += ` - ${error.data}`; + } + + // Append each error message with a newline for separation + systemErrContent += `\n${" ".repeat(6)}${compiledError}`; + }); + + // There'll be a single `CDATA` element compiling all the error messages + if (requestTestSuiteError) { + requestTestSuiteError.dat(systemErrContent); + } + + let requestTestCases = 0; + let erroredRequestTestCases = 0; + let failedRequestTestCases = 0; + + // Test suites correspond to `pw.test()` invocations + testSuites.forEach(({ descriptor, expectResults }) => { + requestTestCases += expectResults.length; + + expectResults.forEach(({ status, message }) => { + const testCase = requestTestSuite + .ele("testcase", { + name: `${descriptor} - ${message}`, + }) + .att("classname", path); + + if (status === "fail") { + failedRequestTestCases += 1; + + testCase + .ele("failure") + .att("type", "AssertionFailure") + .att("message", message); + } else if (status === "error") { + erroredRequestTestCases += 1; + + testCase.ele("error").att("message", message); + } + }); + }); + + requestTestSuite.att("tests", requestTestCases.toString()); + requestTestSuite.att("failures", failedRequestTestCases.toString()); + requestTestSuite.att("errors", erroredRequestTestCases.toString()); + + return { + failedRequestTestCases, + erroredRequestTestCases, + }; +}; + +/** + * Generates the built JUnit report export at the specified path. + * + * @param {GenerateJUnitReportExportArgs} options - The options to generate the JUnit report export. + * @param {number} options.totalTestCases - The total number of test cases. + * @param {number} options.totalFailedTestCases - The total number of failed test cases. + * @param {number} options.totalErroredTestCases - The total number of errored test cases. + * @param {number} options.testDuration - The total duration of test cases. + * @param {string} options.reporterJUnitExportPath - The path to export the JUnit report. + * @returns {void} + */ +export const generateJUnitReportExport = ({ + totalTestCases, + totalFailedTestCases, + totalErroredTestCases, + testDuration, + reporterJUnitExportPath, +}: GenerateJUnitReportExportArgs) => { + rootEl + .att("tests", totalTestCases.toString()) + .att("failures", totalFailedTestCases.toString()) + .att("errors", totalErroredTestCases.toString()) + .att("time", testDuration.toString()); + + // Convert the XML structure to a string + const xmlDocString = rootEl.end({ prettyPrint: true }); + + // Write the XML string to the specified path + try { + const resolvedExportPath = path.resolve(reporterJUnitExportPath); + + if (fs.existsSync(resolvedExportPath)) { + info( + INFO(`\nOverwriting the pre-existing path: ${reporterJUnitExportPath}.`) + ); + } + + fs.mkdirSync(path.dirname(resolvedExportPath), { + recursive: true, + }); + + fs.writeFileSync(resolvedExportPath, xmlDocString); + + log( + SUCCESS( + `\nSuccessfully exported the JUnit report to: ${reporterJUnitExportPath}.` + ) + ); + } catch (err) { + const data = err instanceof Error ? err.message : null; + throw error({ + code: "REPORT_EXPORT_FAILED", + data, + path: reporterJUnitExportPath, + }); + } +}; diff --git a/packages/hoppscotch-cli/src/utils/request.ts b/packages/hoppscotch-cli/src/utils/request.ts index 81513bd21..16a02c519 100644 --- a/packages/hoppscotch-cli/src/utils/request.ts +++ b/packages/hoppscotch-cli/src/utils/request.ts @@ -52,10 +52,11 @@ const processVariables = (variable: Environment["variables"][number]) => { * @param envs Global + selected envs used by requests with in collection * @returns Processed envs with each variable processed */ -const processEnvs = (envs: HoppEnvs) => { +const processEnvs = (envs: Partial) => { + // This can take the shape `{ global: undefined, selected: undefined }` when no environment is supplied const processedEnvs = { - global: envs.global.map(processVariables), - selected: envs.selected.map(processVariables), + global: envs.global?.map(processVariables), + selected: envs.selected?.map(processVariables), }; return processedEnvs; @@ -270,7 +271,7 @@ export const processRequest = // Updating report for errors & current result report.errors.push(preRequestRes.left); - report.result = report.result && false; + report.result = report.result; } else { // Updating effective-request and consuming updated envs after pre-request script execution ({ effectiveRequest, updatedEnvs } = preRequestRes.right); @@ -298,7 +299,7 @@ export const processRequest = if (E.isLeft(requestRunnerRes)) { // Updating report for errors & current result report.errors.push(requestRunnerRes.left); - report.result = report.result && false; + report.result = report.result; printRequestRunner.fail(); } else { @@ -321,7 +322,7 @@ export const processRequest = // Updating report with current errors & result. report.errors.push(testRunnerRes.left); - report.result = report.result && false; + report.result = report.result; } else { const { envs, testsReport, duration } = testRunnerRes.right; const _hasFailedTestCases = hasFailedTestCases(testsReport); diff --git a/packages/hoppscotch-cli/tsconfig.json b/packages/hoppscotch-cli/tsconfig.json index 0d8095b07..d360c14f0 100644 --- a/packages/hoppscotch-cli/tsconfig.json +++ b/packages/hoppscotch-cli/tsconfig.json @@ -10,7 +10,8 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "composite": true + "composite": true, + "lib": ["ESNext", "DOM"], }, "files": ["package.json"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afd2bca5b..7acc1c085 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -324,6 +324,9 @@ importers: verzod: specifier: 0.2.2 version: 0.2.2(zod@3.22.4) + xmlbuilder2: + specifier: 3.1.1 + version: 3.1.1 zod: specifier: 3.22.4 version: 3.22.4 @@ -3513,8 +3516,8 @@ packages: resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==} engines: {node: '>= 14'} - '@intlify/message-compiler@10.0.0-beta.2': - resolution: {integrity: sha512-2yl340oNiCDjdpPIfo49a7o56ZTc+35iN7mxdQvXOLxJ6Pdh9p0GkB0duV44hfHaAxsD1cGQ4naLDRokKUdyvQ==} + '@intlify/message-compiler@10.0.0-beta.5': + resolution: {integrity: sha512-hLLchnM1dmtSEruerkzvU9vePsLqBXz3RU85SCx/Vd12fFQiymP+/5Rn9MJ8MyfLmIOLDEx4PRh+/GkIQP6oog==} engines: {node: '>= 16'} '@intlify/message-compiler@9.13.1': @@ -3533,8 +3536,8 @@ packages: resolution: {integrity: sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ==} engines: {node: '>= 16'} - '@intlify/shared@10.0.0-beta.2': - resolution: {integrity: sha512-u3ey3jn7VZl8SfuBH1nZC1xvdu59/PYkjR6UjXMWiDoowmr/PuPzmIRmOD8jZvRfCbKnKWYRK33HN4uTWLr/Yw==} + '@intlify/shared@10.0.0-beta.5': + resolution: {integrity: sha512-g9bq5Y1bOcC9qxtNk4UWtF3sXm6Wh0fGISb7vD5aLyF7yQv7ZFjxQjJzBP2GqG/9+PAGYutqjP1GGadNqFtyAQ==} engines: {node: '>= 16'} '@intlify/shared@9.13.1': @@ -3994,6 +3997,22 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@oozcitak/dom@1.15.10': + resolution: {integrity: sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==} + engines: {node: '>=8.0'} + + '@oozcitak/infra@1.0.8': + resolution: {integrity: sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==} + engines: {node: '>=6.0'} + + '@oozcitak/url@1.0.4': + resolution: {integrity: sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==} + engines: {node: '>=8.0'} + + '@oozcitak/util@8.3.8': + resolution: {integrity: sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==} + engines: {node: '>=8.0'} + '@peculiar/asn1-schema@2.3.8': resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} @@ -12304,6 +12323,10 @@ packages: resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} engines: {node: '>=4.0.0'} + xmlbuilder2@3.1.1: + resolution: {integrity: sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==} + engines: {node: '>=12.0'} + xmlbuilder@11.0.1: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} @@ -15479,8 +15502,8 @@ snapshots: '@intlify/bundle-utils@3.4.0(vue-i18n@9.8.0(vue@3.3.9(typescript@5.3.2)))': dependencies: - '@intlify/message-compiler': 10.0.0-beta.2 - '@intlify/shared': 10.0.0-beta.2 + '@intlify/message-compiler': 10.0.0-beta.5 + '@intlify/shared': 10.0.0-beta.5 jsonc-eslint-parser: 1.4.1 source-map: 0.6.1 yaml-eslint-parser: 0.3.2 @@ -15533,9 +15556,9 @@ snapshots: dependencies: '@intlify/shared': 9.2.2 - '@intlify/message-compiler@10.0.0-beta.2': + '@intlify/message-compiler@10.0.0-beta.5': dependencies: - '@intlify/shared': 10.0.0-beta.2 + '@intlify/shared': 10.0.0-beta.5 source-map-js: 1.2.0 '@intlify/message-compiler@9.13.1': @@ -15558,7 +15581,7 @@ snapshots: '@intlify/shared': 9.8.0 source-map-js: 1.2.0 - '@intlify/shared@10.0.0-beta.2': {} + '@intlify/shared@10.0.0-beta.5': {} '@intlify/shared@9.13.1': {} @@ -15591,7 +15614,7 @@ snapshots: '@intlify/vite-plugin-vue-i18n@6.0.1(vite@4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.31.0))(vue-i18n@9.8.0(vue@3.3.9(typescript@4.9.5)))': dependencies: '@intlify/bundle-utils': 7.0.0(vue-i18n@9.8.0(vue@3.3.9(typescript@4.9.5))) - '@intlify/shared': 10.0.0-beta.2 + '@intlify/shared': 10.0.0-beta.5 '@rollup/pluginutils': 4.2.1 debug: 4.3.4(supports-color@9.4.0) fast-glob: 3.3.2 @@ -15605,7 +15628,7 @@ snapshots: '@intlify/vite-plugin-vue-i18n@7.0.0(vite@4.5.0(@types/node@18.18.8)(sass@1.69.5)(terser@5.31.0))(vue-i18n@9.8.0(vue@3.3.9(typescript@5.3.2)))': dependencies: '@intlify/bundle-utils': 3.4.0(vue-i18n@9.8.0(vue@3.3.9(typescript@5.3.2))) - '@intlify/shared': 10.0.0-beta.2 + '@intlify/shared': 10.0.0-beta.5 '@rollup/pluginutils': 4.2.1 debug: 4.3.4(supports-color@9.4.0) fast-glob: 3.3.2 @@ -16177,6 +16200,23 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@oozcitak/dom@1.15.10': + dependencies: + '@oozcitak/infra': 1.0.8 + '@oozcitak/url': 1.0.4 + '@oozcitak/util': 8.3.8 + + '@oozcitak/infra@1.0.8': + dependencies: + '@oozcitak/util': 8.3.8 + + '@oozcitak/url@1.0.4': + dependencies: + '@oozcitak/infra': 1.0.8 + '@oozcitak/util': 8.3.8 + + '@oozcitak/util@8.3.8': {} + '@peculiar/asn1-schema@2.3.8': dependencies: asn1js: 3.0.5 @@ -26967,6 +27007,13 @@ snapshots: sax: 1.3.0 xmlbuilder: 11.0.1 + xmlbuilder2@3.1.1: + dependencies: + '@oozcitak/dom': 1.15.10 + '@oozcitak/infra': 1.0.8 + '@oozcitak/util': 8.3.8 + js-yaml: 3.14.1 + xmlbuilder@11.0.1: {} xmlchars@2.2.0: