diff --git a/README.md b/README.md index 41cd991..48cfc07 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Salesforce packages follow this structure: sf plugins install sf-package-combiner@x.y.z ``` -## Commands +## Command @@ -61,3 +61,44 @@ EXAMPLES ``` + +## Parsing Strings with `package.xml` contents + +Currently, I'm working on a feature to allow strings containing package.xml contents to be accepted through the terminal using a new command flag. + +Until that is implemented, you could use this simple shell script which could read a string stored in a variable which contains package.xml contents and create a temporary package.xml from that. That temporary package.xml then could be read by this plugin and overwritten as the combined package. + +In my 1 use case, the `$COMMIT_MSG` variable is GitLab's predefined variable named `$CI_COMMIT_MESSAGE` which contains the commit message. + +```bash +#!/bin/bash +set -e + +DEPLOY_PACKAGE="package.xml" + +# Define a function to build package.xml from commit message +build_package_from_commit() { + local commit_msg="$1" + local output_file="$2" + PACKAGE_FOUND="False" + + # Use sed to match and extract the XML package content + package_xml_content=$(echo "$commit_msg" | sed -n '//,/<\/Package>/p') + + if [[ -n "$package_xml_content" ]]; then + echo "Found package.xml contents in the commit message." + echo "$package_xml_content" > "$output_file" + PACKAGE_FOUND="True" + else + echo "WARNING: Package.xml contents NOT found in the commit message." + fi + export PACKAGE_FOUND +} + +build_package_from_commit "$COMMIT_MSG" "$DEPLOY_PACKAGE" + +# combines the sfdx-git-delta package.xml with the package found in the commit message +if [[ "$PACKAGE_FOUND" == "True" ]]; then + sf sfpc combine -f "package/package.xml" -f "$DEPLOY_PACKAGE" -c "$DEPLOY_PACKAGE" +fi +``` diff --git a/package.json b/package.json index 0df066b..d786284 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,13 @@ "@oclif/core": "^4", "@salesforce/core": "^8", "@salesforce/sf-plugins-core": "^12", - "xml2js": "^0.6.2", - "xmlbuilder2": "^3.1.1" + "fast-xml-parser": "^4.5.0" }, "devDependencies": { "@oclif/plugin-command-snapshot": "^5.1.9", "@salesforce/cli-plugins-testkit": "^5.3.10", "@salesforce/dev-scripts": "^10", "@types/mocha": "^10.0.9", - "@types/xml2js": "^0.4.14", "eslint-plugin-sf-plugin": "^1.18.6", "husky": "^9.1.6", "mocha": "^10.8.2", diff --git a/src/commands/sfpc/combine.ts b/src/commands/sfpc/combine.ts index 4c72bf5..bd80e15 100644 --- a/src/commands/sfpc/combine.ts +++ b/src/commands/sfpc/combine.ts @@ -3,7 +3,7 @@ import { writeFile } from 'node:fs/promises'; import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; import { Messages } from '@salesforce/core'; -import { SalesforcePackageXml, SfpcCombineResult } from '../../helpers/types.js'; +import { PackageXmlObject, SfpcCombineResult } from '../../helpers/types.js'; import { buildPackage } from '../../helpers/buildPackage.js'; import { readPackageFiles } from '../../helpers/readPackageFiles.js'; @@ -35,7 +35,7 @@ export default class SfpcCombine extends SfCommand { const files = flags['package-file'] ?? null; const combinedPackage = flags['combined-package']; - let packageContents: SalesforcePackageXml[] = []; + let packageContents: PackageXmlObject[] = []; let apiVersions: string[] = []; let warnings: string[] = []; diff --git a/src/helpers/buildPackage.ts b/src/helpers/buildPackage.ts index 71605cd..37c401b 100644 --- a/src/helpers/buildPackage.ts +++ b/src/helpers/buildPackage.ts @@ -1,15 +1,21 @@ -import { create } from 'xmlbuilder2'; +import { XMLBuilder } from 'fast-xml-parser'; -import { SalesforcePackageXml } from './types.js'; +import { PackageXmlObject } from './types.js'; -const xmlConf = { indent: ' ', newline: '\n', prettyPrint: true }; +const xmlConf = { + format: true, + indentBy: ' ', + suppressEmptyNode: false, + ignoreAttributes: false, + attributeNamePrefix: '@_', +}; -export function buildPackage(packageContents: SalesforcePackageXml[], apiVersions: string[]): string { +export function buildPackage(packageContents: PackageXmlObject[], apiVersions: string[]): string { // Determine the maximum API version from the apiVersions array const maxVersion = apiVersions.reduce((max, version) => (version > max ? version : max), '0.0'); // Combine the parsed package.xml contents - const mergedPackage: SalesforcePackageXml = { Package: { types: [], version: maxVersion } }; + const mergedPackage: PackageXmlObject = { Package: { types: [], version: maxVersion } }; // Process each parsed package XML for (const pkg of packageContents) { @@ -33,31 +39,31 @@ export function buildPackage(packageContents: SalesforcePackageXml[], apiVersion } mergedPackage.Package.types.sort((a, b) => a.name.localeCompare(b.name)); - const root = create({ version: '1.0', encoding: 'UTF-8' }).ele('Package', { - xmlns: 'http://soap.sforce.com/2006/04/metadata', - }); - - // Create for each type, properly formatting the XML - if (packageContents.length > 0) { - mergedPackage.Package.types.forEach((type) => { - const typeElement = root.ele('types'); - type.members - .sort((a, b) => a.localeCompare(b)) - .forEach((member) => { - typeElement.ele('members').txt(member).up(); - }); - typeElement.ele('name').txt(type.name).up(); - }); - } else { - root.txt('\n'); - root.txt('\n'); - } + // Construct the XML data as a JSON-like object + const packageXmlObject: PackageXmlObject = { + Package: { + '@_xmlns': 'http://soap.sforce.com/2006/04/metadata', + types: mergedPackage.Package.types.map((type) => ({ + members: type.members.sort((a, b) => a.localeCompare(b)), + name: type.name, + })), + version: maxVersion !== '0.0' ? maxVersion : undefined, + }, + }; + + // Build the XML string + const builder = new XMLBuilder(xmlConf); + let xmlContent = builder.build(packageXmlObject) as string; - // Set the maximum version element - if (maxVersion !== '0.0') { - root.ele('version').txt(maxVersion); + // Ensure formatting for an empty package + if (mergedPackage.Package.types.length === 0) { + xmlContent = xmlContent.replace( + '', + '\n\n' + ); } - // Output the merged package.xml - return root.end(xmlConf); + // Prepend the XML declaration manually + const xmlHeader = '\n'; + return xmlHeader + xmlContent; } diff --git a/src/helpers/parsePackage.ts b/src/helpers/parsePackage.ts index 95ac685..da8f0a0 100644 --- a/src/helpers/parsePackage.ts +++ b/src/helpers/parsePackage.ts @@ -1,66 +1,88 @@ /* eslint-disable no-await-in-loop */ -import { Parser } from 'xml2js'; +import { XMLParser, XMLValidator } from 'fast-xml-parser'; -import { SalesforcePackageXml } from './types.js'; +import { PackageXmlObject } from './types.js'; -// Safe parsing function for package XML using xml2js -export async function parsePackageXml(xmlContent: string): Promise { +const XML_PARSER_OPTION = { + ignoreAttributes: false, + attributeNamePrefix: '@_', + parseTagValue: false, + parseNodeValue: false, + parseAttributeValue: false, + trimValues: true, +}; + +export function parsePackageXml(xmlContent: string): PackageXmlObject | null { try { - const parser = new Parser(); + // Validate the XML content + const validationResult = XMLValidator.validate(xmlContent); + if (validationResult !== true) { + return null; + } + + const parser = new XMLParser(XML_PARSER_OPTION); // Parse the XML string to an object - const parsed = (await parser.parseStringPromise(xmlContent)) as unknown as SalesforcePackageXml; + const parsed = parser.parse(xmlContent) as unknown as PackageXmlObject; - // Ensure the root exists - if (!parsed.Package) { + // Ensure the root exists and is of correct type + if (!parsed || typeof parsed !== 'object' || !parsed.Package) { return null; } - // Validate that the root contains only allowed keys (, ) - const allowedKeys = new Set(['types', 'version']); - const packageKeys = Object.keys(parsed.Package).filter((key) => key !== '$'); // Ignore the '$' key + const packageData = parsed.Package as Partial; + // Validate and normalize the field + if (!packageData.types) { + return null; + } + const allowedKeys = new Set(['types', 'version']); + const packageKeys = Object.keys(parsed.Package).filter((key) => key !== '@_xmlns'); const hasUnexpectedKeys = packageKeys.some((key) => !allowedKeys.has(key)); if (hasUnexpectedKeys) { return null; } + const normalizedTypes = Array.isArray(packageData.types) ? packageData.types : [packageData.types]; - parsed.Package.types = parsed.Package.types.map((type) => { - // Validate that there is exactly one element - if (!Array.isArray(type.name) || type.name.length !== 1 || typeof type.name[0] !== 'string') { - throw new Error('Invalid package.xml: Each block must have exactly one element.'); + packageData.types = normalizedTypes.map((type): { name: string; members: string[] } => { + if (!type || typeof type !== 'object' || typeof type.name !== 'string') { + throw new Error('Invalid block: Missing or invalid element.'); } // Validate that only "name" and "members" keys are present const allowedTypesKeys = new Set(['name', 'members']); const typeKeys = Object.keys(type); const hasUnexpectedTypesKeys = typeKeys.some((key) => !allowedTypesKeys.has(key)); - if (hasUnexpectedTypesKeys) { throw new Error('Invalid package.xml: Each block must contain only and tags.'); } - const name = type.name[0]; - const members = Array.isArray(type.members) ? type.members.flat() : type.members; + // Ensure members is always a string array + let members: string[]; + + if (Array.isArray(type.members)) { + members = type.members.filter((member): member is string => typeof member === 'string'); + } else if (typeof type.members === 'string') { + members = [type.members]; + } else { + members = []; + } + return { - ...type, - name, + name: type.name, members, }; }); - // Enforce a maximum of one tag in the package.xml - if (parsed.Package && Array.isArray(parsed.Package.version)) { - if (parsed.Package.version.length > 1) { - return null; // Invalid structure, more than one tag - } - // Convert to a single string if only one tag is present - if (Array.isArray(parsed.Package.version) && typeof parsed.Package.version[0] === 'string') { - parsed.Package.version = parsed.Package.version[0]; + // Ensure a maximum of one tag + if (Array.isArray(packageData.version)) { + if (packageData.version.length > 1) { + return null; } + packageData.version = packageData.version[0] as string; } - // Apply a type guard to safely assert the parsed content matches SalesforcePackageXml - if (isSalesforcePackageXml(parsed)) { - return parsed; + // Validate the final structure + if (isPackageXmlObject({ Package: packageData })) { + return { Package: packageData as PackageXmlObject['Package'] }; } else { return null; } @@ -69,23 +91,35 @@ export async function parsePackageXml(xmlContent: string): Promise } }).Package.types.every( +function isPackageXmlObject(obj: unknown): obj is PackageXmlObject { + if ( + typeof obj !== 'object' || + obj === null || + !('Package' in obj) || + typeof (obj as { Package: unknown }).Package !== 'object' + ) { + return false; + } + + const packageData = (obj as { Package: unknown }).Package as Partial; + + if ( + !Array.isArray(packageData.types) || + !packageData.types.every( (type) => typeof type === 'object' && type !== null && typeof type.name === 'string' && Array.isArray(type.members) && type.members.every((member) => typeof member === 'string') - ) && - // Make version optional here: allow it to be a string or undefined - (typeof (obj as { Package: { version?: unknown } }).Package.version === 'string' || - typeof (obj as { Package: { version?: unknown } }).Package.version === 'undefined') - ); + ) + ) { + return false; + } + + if (packageData.version && typeof packageData.version !== 'string') { + return false; + } + + return true; } diff --git a/src/helpers/readPackageFiles.ts b/src/helpers/readPackageFiles.ts index e90f3fa..93ab065 100644 --- a/src/helpers/readPackageFiles.ts +++ b/src/helpers/readPackageFiles.ts @@ -2,19 +2,19 @@ import { readFile } from 'node:fs/promises'; import { parsePackageXml } from './parsePackage.js'; -import { SalesforcePackageXml } from './types.js'; +import { PackageXmlObject } from './types.js'; export async function readPackageFiles( files: string[] | null -): Promise<{ packageContents: SalesforcePackageXml[]; apiVersions: string[]; warnings: string[] }> { +): Promise<{ packageContents: PackageXmlObject[]; apiVersions: string[]; warnings: string[] }> { const warnings: string[] = []; - const packageContents: SalesforcePackageXml[] = []; + const packageContents: PackageXmlObject[] = []; const apiVersions: string[] = []; if (files) { for (const filePath of files) { try { const fileContent = await readFile(filePath, 'utf-8'); - const parsed = await parsePackageXml(fileContent); + const parsed = parsePackageXml(fileContent); if (parsed) { packageContents.push(parsed); // Add the package version to the apiVersions array diff --git a/src/helpers/types.ts b/src/helpers/types.ts index 505151e..95ed7a6 100644 --- a/src/helpers/types.ts +++ b/src/helpers/types.ts @@ -2,12 +2,15 @@ export type SfpcCombineResult = { path: string; }; -export type SalesforcePackageXml = { +export type PackageTypeObject = { + name: string; + members: string[]; +}; + +export type PackageXmlObject = { Package: { - types: Array<{ - name: string; - members: string[]; - }>; + '@_xmlns'?: string; + types: PackageTypeObject[]; version?: string; }; }; diff --git a/test/commands/sfpc/combine.nut.ts b/test/commands/sfpc/combine.nut.ts index 582e4dc..a77036c 100644 --- a/test/commands/sfpc/combine.nut.ts +++ b/test/commands/sfpc/combine.nut.ts @@ -9,6 +9,7 @@ describe('sfpc combine NUTs', () => { let session: TestSession; const package1 = resolve('test/samples/pack1.xml'); const package2 = resolve('test/samples/pack2.xml'); + const package3 = resolve('test/samples/pack3.xml'); const outputPackage = resolve('package.xml'); const baseline = resolve('test/samples/combinedPackage.xml'); @@ -21,8 +22,8 @@ describe('sfpc combine NUTs', () => { await rm(outputPackage); }); - it('combine the 2 packages together', () => { - const command = `sfpc combine -f ${package1} -f ${package2} -c ${outputPackage}`; + it('combine the valid packages together.', () => { + const command = `sfpc combine -f ${package1} -f ${package2} -f ${package3} -c ${outputPackage}`; const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout; expect(output).to.contain(`Combined package.xml written to: ${outputPackage}`); }); diff --git a/test/commands/sfpc/combine.test.ts b/test/commands/sfpc/combine.test.ts index 4b4329e..ac597fa 100644 --- a/test/commands/sfpc/combine.test.ts +++ b/test/commands/sfpc/combine.test.ts @@ -12,8 +12,13 @@ describe('sfpc combine', () => { let sfCommandStubs: ReturnType; const package1 = resolve('test/samples/pack1.xml'); const package2 = resolve('test/samples/pack2.xml'); + const package3 = resolve('test/samples/pack3.xml'); + const invalidPackage1 = resolve('test/samples/invalid1.xml'); + const invalidPackage2 = resolve('test/samples/invalid2.xml'); + const invalidPackage3 = resolve('test/samples/invalid3.xml'); const outputPackage = resolve('package.xml'); const baseline = resolve('test/samples/combinedPackage.xml'); + const emptyPackageBaseline = resolve('test/samples/emptyPackage.xml'); beforeEach(() => { sfCommandStubs = stubSfCommandUx($$.SANDBOX); @@ -27,8 +32,8 @@ describe('sfpc combine', () => { await rm(outputPackage); }); - it('combine the 2 packages together', async () => { - await SfpcCombine.run(['-f', package1, '-f', package2, '-c', outputPackage]); + it('combine the valid packages together.', async () => { + await SfpcCombine.run(['-f', package1, '-f', package2, '-f', package3, '-c', outputPackage]); const output = sfCommandStubs.log .getCalls() .flatMap((c) => c.args) @@ -40,4 +45,70 @@ describe('sfpc combine', () => { const baselinePackage = await readFile(baseline, 'utf-8'); strictEqual(testPackage, baselinePackage, `File content is different between ${outputPackage} and ${baseline}`); }); + it('test the invalid packages.', async () => { + await SfpcCombine.run(['-f', invalidPackage1, '-c', outputPackage]); + const output = sfCommandStubs.log + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(output).to.include(`Combined package.xml written to: ${outputPackage}`); + const warnings = sfCommandStubs.warn + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(warnings).to.include(`File ${invalidPackage1} does not match expected Salesforce package structure.`); + }); + it('confirm the invalid XML is an empty package.', async () => { + const testPackage = await readFile(outputPackage, 'utf-8'); + const baselinePackage = await readFile(emptyPackageBaseline, 'utf-8'); + strictEqual( + testPackage, + baselinePackage, + `File content is different between ${outputPackage} and ${emptyPackageBaseline}` + ); + }); + it('test the invalid packages.', async () => { + await SfpcCombine.run(['-f', invalidPackage2, '-c', outputPackage]); + const output = sfCommandStubs.log + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(output).to.include(`Combined package.xml written to: ${outputPackage}`); + const warnings = sfCommandStubs.warn + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(warnings).to.include(`File ${invalidPackage2} does not match expected Salesforce package structure.`); + }); + it('confirm the invalid XML is an empty package.', async () => { + const testPackage = await readFile(outputPackage, 'utf-8'); + const baselinePackage = await readFile(emptyPackageBaseline, 'utf-8'); + strictEqual( + testPackage, + baselinePackage, + `File content is different between ${outputPackage} and ${emptyPackageBaseline}` + ); + }); + it('test the invalid packages.', async () => { + await SfpcCombine.run(['-f', invalidPackage3, '-c', outputPackage]); + const output = sfCommandStubs.log + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(output).to.include(`Combined package.xml written to: ${outputPackage}`); + const warnings = sfCommandStubs.warn + .getCalls() + .flatMap((c) => c.args) + .join('\n'); + expect(warnings).to.include(`File ${invalidPackage3} does not match expected Salesforce package structure.`); + }); + it('confirm the invalid XML is an empty package.', async () => { + const testPackage = await readFile(outputPackage, 'utf-8'); + const baselinePackage = await readFile(emptyPackageBaseline, 'utf-8'); + strictEqual( + testPackage, + baselinePackage, + `File content is different between ${outputPackage} and ${emptyPackageBaseline}` + ); + }); }); diff --git a/test/samples/combinedPackage.xml b/test/samples/combinedPackage.xml index ba97e65..aa31516 100644 --- a/test/samples/combinedPackage.xml +++ b/test/samples/combinedPackage.xml @@ -1,5 +1,9 @@ + + Auto + customlabel + Account Case @@ -10,4 +14,4 @@ standardvalueset 59.0 - \ No newline at end of file + diff --git a/test/samples/emptyPackage.xml b/test/samples/emptyPackage.xml new file mode 100644 index 0000000..db6398d --- /dev/null +++ b/test/samples/emptyPackage.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/samples/invalid1.xml b/test/samples/invalid1.xml new file mode 100644 index 0000000..c7e217e --- /dev/null +++ b/test/samples/invalid1.xml @@ -0,0 +1,12 @@ + + + + Account + CustomObject + + + Account.Rating__c + CustomField + + 57.0 + \ No newline at end of file diff --git a/test/samples/invalid2.xml b/test/samples/invalid2.xml new file mode 100644 index 0000000..86387ca --- /dev/null +++ b/test/samples/invalid2.xml @@ -0,0 +1,13 @@ + + + + Account + CustomObject + + + Account.Rating__c + CustomField + + 57.0 + 59.0 + \ No newline at end of file diff --git a/test/samples/invalid3.xml b/test/samples/invalid3.xml new file mode 100644 index 0000000..6b37227 --- /dev/null +++ b/test/samples/invalid3.xml @@ -0,0 +1,12 @@ + + + + CustomObject + + + Account.Rating__c + CustomField + CustomField + + 57.0 + \ No newline at end of file diff --git a/test/samples/pack3.xml b/test/samples/pack3.xml new file mode 100644 index 0000000..6fce49a --- /dev/null +++ b/test/samples/pack3.xml @@ -0,0 +1,7 @@ + + + + Auto + customlabel + + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index cbd7147..c73aa8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1703,35 +1703,6 @@ strip-ansi "^7.1.0" wrap-ansi "^9.0.0" -"@oozcitak/dom@1.15.10": - version "1.15.10" - resolved "https://registry.yarnpkg.com/@oozcitak/dom/-/dom-1.15.10.tgz#dca7289f2b292cff2a901ea4fbbcc0a1ab0b05c2" - integrity sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ== - dependencies: - "@oozcitak/infra" "1.0.8" - "@oozcitak/url" "1.0.4" - "@oozcitak/util" "8.3.8" - -"@oozcitak/infra@1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@oozcitak/infra/-/infra-1.0.8.tgz#b0b089421f7d0f6878687608301fbaba837a7d17" - integrity sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg== - dependencies: - "@oozcitak/util" "8.3.8" - -"@oozcitak/url@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@oozcitak/url/-/url-1.0.4.tgz#ca8b1c876319cf5a648dfa1123600a6aa5cda6ba" - integrity sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw== - dependencies: - "@oozcitak/infra" "1.0.8" - "@oozcitak/util" "8.3.8" - -"@oozcitak/util@8.3.8": - version "8.3.8" - resolved "https://registry.yarnpkg.com/@oozcitak/util/-/util-8.3.8.tgz#10f65fe1891fd8cde4957360835e78fd1936bfdd" - integrity sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ== - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -2635,13 +2606,6 @@ resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== -"@types/xml2js@^0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.14.tgz#5d462a2a7330345e2309c6b549a183a376de8f9a" - integrity sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin@^6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" @@ -4332,6 +4296,13 @@ fast-xml-parser@4.4.1: dependencies: strnum "^1.0.5" +fast-xml-parser@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" + integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== + dependencies: + strnum "^1.0.5" + fastest-levenshtein@^1.0.7: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -5411,14 +5382,6 @@ joycon@^3.1.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.13.1, js-yaml@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -5426,6 +5389,14 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +js-yaml@^3.13.1, js-yaml@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js2xmlparser@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a" @@ -7405,16 +7376,7 @@ stack-utils@^2.0.6: dependencies: escape-string-regexp "^2.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7482,14 +7444,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -8083,7 +8038,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -8101,15 +8056,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -8156,16 +8102,6 @@ xml2js@^0.6.2: sax ">=0.6.0" xmlbuilder "~11.0.0" -xmlbuilder2@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz#b977ef8a6fb27a1ea7ffa7d850d2c007ff343bc0" - integrity sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw== - dependencies: - "@oozcitak/dom" "1.15.10" - "@oozcitak/infra" "1.0.8" - "@oozcitak/util" "8.3.8" - js-yaml "3.14.1" - xmlbuilder@~11.0.0: version "11.0.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"