From 9df47777d003b6ec6f8bef2eb09d282d50d9dbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:14:53 -0600 Subject: [PATCH] feat: build assets --- .vscode/settings.json | 2 + package.json | 2 +- scripts/buildAssets.ts | 441 +++++++++++------- scripts/buildRatingsLeaderboard.ts | 2 +- src/components/Tanks/components/Item.tsx | 8 +- src/core/blitz/readStringDVPL.ts | 6 + src/core/blitz/readXMLDVPL.ts | 9 + src/core/blitz/readYAMLDVPL.ts | 8 + .../core/blitzkrieg}/commitMultipleFiles.ts | 14 +- src/core/blitzkrieg/definitions/tanks.ts | 24 +- tsconfig.json | 2 +- yarn.lock | 24 +- 12 files changed, 328 insertions(+), 214 deletions(-) create mode 100644 src/core/blitz/readStringDVPL.ts create mode 100644 src/core/blitz/readXMLDVPL.ts create mode 100644 src/core/blitz/readYAMLDVPL.ts rename {scripts => src/core/blitzkrieg}/commitMultipleFiles.ts (87%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 12fd07bf9..ceab1196e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,6 +58,8 @@ "webp", "winrate", "wotblitz", + "XMLDVPL", + "YAMLDVPL", "zustand" ], "deno.enable": false, diff --git a/package.json b/package.json index 2407827c9..7dafc306f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "vite-node": "^0.34.5", "vitepress": "^1.0.0-rc.20", "vitepress-plugin-auto-sidebar": "^1.1.0", - "xml-js": "^1.6.11", "yaml": "^2.3.4", "zustand": "^4.4.1" }, @@ -72,6 +71,7 @@ "@vanilla-extract/next-plugin": "^2.3.1", "compare-versions": "^6.1.0", "crc": "^4.3.2", + "fast-xml-parser": "^4.3.2", "knip": "^3.4.0", "lz4": "^0.6.5", "prettier": "^3.0.3", diff --git a/scripts/buildAssets.ts b/scripts/buildAssets.ts index 8c563b980..a968edc14 100644 --- a/scripts/buildAssets.ts +++ b/scripts/buildAssets.ts @@ -1,33 +1,50 @@ import { Octokit } from '@octokit/rest'; import { config } from 'dotenv'; -import { writeFileSync } from 'fs'; -import { readFile, readdir } from 'fs/promises'; -import { load } from 'js-yaml'; +import { readdir } from 'fs/promises'; import { argv, env } from 'process'; -import { ElementCompact, xml2js } from 'xml-js'; -import { parse } from 'yaml'; +import { TankType } from '../src/components/Tanks'; import { NATION_IDS } from '../src/constants/nations'; -import { readDVPL } from '../src/core/blitz/readDVPL'; +import { readXMLDVPL } from '../src/core/blitz/readXMLDVPL'; +import { readYAMLDVPL } from '../src/core/blitz/readYAMLDVPL'; +import commitMultipleFiles from '../src/core/blitzkrieg/commitMultipleFiles'; import { TankDefinitions, Tier, } from '../src/core/blitzkrieg/definitions/tanks'; -import commitMultipleFiles, { FileChange } from './commitMultipleFiles'; config(); +interface VehicleDefinitionList { + [key: string]: { + id: number; + userString: string; + shortUserString?: string; + description: string; + price: number | { gold: ''; '#text': number }; + sellPrice?: { gold: ''; '#text': number }; + enrichmentPermanentCost: number; + notInShop?: boolean; + tags: string; + level: number; + combatRole?: Record; + configurationModes: string; + }; +} +interface Strings { + [key: string]: string; +} +type BlitzTankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank'; + +const blitzTankTypeToBlitzkrieg: Record = { + 'AT-SPG': 'tank_destroyer', + lightTank: 'light', + mediumTank: 'medium', + heavyTank: 'heavy', +}; const DATA = 'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data'; const LANGUAGE = 'en'; - -const targets = argv - .find((argument) => argument.startsWith('--target')) - ?.split('=')[1] - .split(','); - -if (!targets) throw new Error('No target(s) specified'); - -const directoriesOfInterest = { +const DOI = { vehicleDefinitions: 'XML/item_defs/vehicles', strings: 'Strings', tankParameters: '3d/Tanks/Parameters', @@ -36,171 +53,251 @@ const directoriesOfInterest = { flags: 'Gfx/Lobby/flags', }; -console.log( - ( - await readDVPL( - `${DATA}/${directoriesOfInterest.strings}/${LANGUAGE}.yaml.dvpl`, - ) - ).toString(), -); -throw new Error('stop'); +const octokit = new Octokit({ auth: env.GH_TOKEN }); +const targets = argv + .find((argument) => argument.startsWith('--target')) + ?.split('=')[1] + .split(','); -const strings = await readFile( - `${DATA}/${directoriesOfInterest.strings}/${LANGUAGE}.yaml.dvpl`, - 'utf-8', -).then((data) => load(data) as Record); +if (!targets) throw new Error('No target(s) specified'); -const tankDefinitions: TankDefinitions = {}; -const tankIds: number[] = []; -const images: Record = {}; -const nations = await readdir( - `${TEMP}/${directoriesOfInterest.vehicleDefinitions}`, -); +if (targets.includes('tankDefinitions')) { + console.log('Building tank definitions...'); -writeFileSync('test.json', JSON.stringify(strings, null, 2)); - -Promise.all( - nations - .filter((nation) => nation !== 'common') - .map(async (nation) => { - const listXML = await readFile( - `${TEMP}/${directoriesOfInterest.vehicleDefinitions}/${nation}/list.xml`, - 'utf-8', - ); - const list = xml2js(listXML, { compact: true }) as ElementCompact; - - await Promise.all( - Object.entries(list.root).map( - async ([vehicle, vehicleData]) => { - const nationVehicleId = parseInt(list.root[vehicle].id._text); - const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; - const name = strings[vehicleData.userString._text]; - const name_short = strings[vehicleData.shortUserString?._text]; - const isPremium = 'gold' in vehicleData.price; - const isCollector = vehicleData.sellPrice - ? 'gold' in vehicleData.sellPrice - : false; - const tier = parseInt(vehicleData.level._text) as Tier; - const tags = vehicleData.tags._text.split(' '); - const type = tags[0]; - - if ((name ?? name_short) === undefined) { - console.warn(`Missing name for ${vehicle}`); - } - - tankIds.push(id); - tankDefinitions[id] = { - id, - name, - name_short, - nation, - tree_type: isCollector - ? 'collector' - : isPremium - ? 'premium' - : 'researchable', - tier, - type, - }; - - // Tank images - const parameterPath = `${TEMP}/${directoriesOfInterest.tankParameters}/${nation}/${vehicle}.yaml`; - const parameterString = await readFile(parameterPath, 'utf-8'); - const { smallIconPath, bigIconPath } = - parse(parameterString).resourcesPath; - images[id] = { - small: `${TEMP}/assets/Data/${(smallIconPath as string) - .replace(/~res:\//, '') - .replace(/\..+/, '')}.packed.webp`, - big: `${TEMP}/assets/Data/${(bigIconPath as string) - .replace(/~res:\//, '') - .replace(/\..+/, '')}.packed.webp`, - }; - }, - ), - ); - }), -).then(async () => { - if (!publish) return; - - const octokit = new Octokit({ auth: env.GH_TOKEN }); - // const tankDefinitionsChange: FileChange = { - // content: JSON.stringify(tankDefinitions), - // path: 'definitions/tanks.json', - // encoding: 'utf-8', - // }; - // const iconsChange = ( - // await Promise.all( - // tankIds.map(async (id) => { - // try { - // const [contentSmall, contentBig] = await Promise.all([ - // readFile(images[id].small, { encoding: 'base64' }), - // readFile(images[id].big, { encoding: 'base64' }), - // ]); - - // return [ - // { - // content: contentBig, - // path: `icons/big/${id}.webp`, - // encoding: 'base64', - // }, - // { - // content: contentSmall, - // path: `icons/small/${id}.webp`, - // encoding: 'base64', - // }, - // ]; - // } catch (error) { - // console.log(error); - // return undefined; - // } - // }), - // ) - // ).flat() as FileChange[]; - const flags = await readdir(`${TEMP}/${directoriesOfInterest.flags}`); - // const scratchedFlags = await Promise.all( - // flags - // .filter((flag) => flag.startsWith('flag_tutor-tank_')) - // .map(async (flag) => { - // const content = await readFile( - // `${TEMP}/${directoriesOfInterest.flags}/${flag}`, - // { encoding: 'base64' }, - // ); - // const name = flag.match(/flag_tutor-tank_(.+)\.packed\.webp/)![1]; - - // return { - // content, - // encoding: 'base64', - // path: `flags/scratched/${name}.webp`, - // } satisfies FileChange; - // }), - // ); - const circleFlags = await Promise.all( - flags - .filter((flag) => flag.startsWith('flag_profile-stat_')) - .map(async (flag) => { - const content = await readFile( - `${TEMP}/${directoriesOfInterest.flags}/${flag}`, - { encoding: 'base64' }, - ); - const name = flag.match(/flag_profile-stat_(.+)\.packed\.webp/)![1]; - - return { - content, - encoding: 'base64', - path: `flags/circle/${name}.webp`, - } satisfies FileChange; - }), + const tankDefinitions: TankDefinitions = {}; + const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then( + (nations) => nations.filter((nation) => nation !== 'common'), + ); + const strings = await readYAMLDVPL( + `${DATA}/${DOI.strings}/${LANGUAGE}.yaml.dvpl`, ); - console.log('Writing files...'); + await Promise.all( + nations.map(async (nation) => { + const tanks = await readXMLDVPL( + `${DATA}/${DOI.vehicleDefinitions}/${nation}/list.xml.dvpl`, + ); + + for (const tankIndex in tanks) { + const tank = tanks[tankIndex]; + const nationVehicleId = tank.id; + const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; + const name = strings[tank.userString]; + const name_short = tank.shortUserString + ? strings[tank.shortUserString] === name + ? undefined + : strings[tank.shortUserString] + : undefined; + const isPremium = + typeof tank.price === 'number' ? false : 'gold' in tank.price; + const isCollector = tank.sellPrice ? 'gold' in tank.sellPrice : false; + const tier = tank.level as Tier; + const tags = tank.tags.split(' '); + const type = blitzTankTypeToBlitzkrieg[tags[0] as BlitzTankType]; + + if ((name ?? name_short) === undefined) { + console.warn(`Missing name for ${tankIndex}`); + } + + tankDefinitions[id] = { + id, + name, + name_short, + nation, + tree_type: isCollector + ? 'collector' + : isPremium + ? 'premium' + : 'researchable', + tier, + type, + }; + } + }), + ); + + console.log('Committing tank definitions...'); await commitMultipleFiles( octokit, 'tresabhi', 'blitzkrieg-assets', 'main', - new Date().toString(), - circleFlags, + 'tank definitions', + [ + { + content: JSON.stringify(tankDefinitions), + encoding: 'utf-8', + path: 'definitions/tanks.json', + }, + ], true, ); -}); +} + +// console.log( +// ( +// await readDVPL( +// `${DATA}/${DOI.strings}/${LANGUAGE}.yaml.dvpl`, +// ) +// ).toString(), +// ); + +// const strings = await readFile( +// `${DATA}/${DOI.strings}/${LANGUAGE}.yaml.dvpl`, +// 'utf-8', +// ).then((data) => load(data) as Record); + +// const tankDefinitions: TankDefinitions = {}; +// const tankIds: number[] = []; +// const images: Record = {}; +// const nations = await readdir( +// `${TEMP}/${DOI.vehicleDefinitions}`, +// ); + +// writeFileSync('test.json', JSON.stringify(strings, null, 2)); + +// Promise.all( +// nations +// .filter((nation) => nation !== 'common') +// .map(async (nation) => { +// const listXML = await readFile( +// `${TEMP}/${DOI.vehicleDefinitions}/${nation}/list.xml`, +// 'utf-8', +// ); +// const list = xml2js(listXML, { compact: true }) as ElementCompact; + +// await Promise.all( +// Object.entries(list.root).map( +// async ([vehicle, vehicleData]) => { +// const nationVehicleId = parseInt(list.root[vehicle].id._text); +// const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; +// const name = strings[vehicleData.userString._text]; +// const name_short = strings[vehicleData.shortUserString?._text]; +// const isPremium = 'gold' in vehicleData.price; +// const isCollector = vehicleData.sellPrice +// ? 'gold' in vehicleData.sellPrice +// : false; +// const tier = parseInt(vehicleData.level._text) as Tier; +// const tags = vehicleData.tags._text.split(' '); +// const type = tags[0]; + +// if ((name ?? name_short) === undefined) { +// console.warn(`Missing name for ${vehicle}`); +// } + +// tankIds.push(id); +// tankDefinitions[id] = { +// id, +// name, +// name_short, +// nation, +// tree_type: isCollector +// ? 'collector' +// : isPremium +// ? 'premium' +// : 'researchable', +// tier, +// type, +// }; + +// // Tank images +// const parameterPath = `${TEMP}/${DOI.tankParameters}/${nation}/${vehicle}.yaml`; +// const parameterString = await readFile(parameterPath, 'utf-8'); +// const { smallIconPath, bigIconPath } = +// parse(parameterString).resourcesPath; +// images[id] = { +// small: `${TEMP}/assets/Data/${(smallIconPath as string) +// .replace(/~res:\//, '') +// .replace(/\..+/, '')}.packed.webp`, +// big: `${TEMP}/assets/Data/${(bigIconPath as string) +// .replace(/~res:\//, '') +// .replace(/\..+/, '')}.packed.webp`, +// }; +// }, +// ), +// ); +// }), +// ).then(async () => { +// if (!publish) return; + +// const octokit = new Octokit({ auth: env.GH_TOKEN }); +// // const tankDefinitionsChange: FileChange = { +// // content: JSON.stringify(tankDefinitions), +// // path: 'definitions/tanks.json', +// // encoding: 'utf-8', +// // }; +// // const iconsChange = ( +// // await Promise.all( +// // tankIds.map(async (id) => { +// // try { +// // const [contentSmall, contentBig] = await Promise.all([ +// // readFile(images[id].small, { encoding: 'base64' }), +// // readFile(images[id].big, { encoding: 'base64' }), +// // ]); + +// // return [ +// // { +// // content: contentBig, +// // path: `icons/big/${id}.webp`, +// // encoding: 'base64', +// // }, +// // { +// // content: contentSmall, +// // path: `icons/small/${id}.webp`, +// // encoding: 'base64', +// // }, +// // ]; +// // } catch (error) { +// // console.log(error); +// // return undefined; +// // } +// // }), +// // ) +// // ).flat() as FileChange[]; +// const flags = await readdir(`${TEMP}/${DOI.flags}`); +// // const scratchedFlags = await Promise.all( +// // flags +// // .filter((flag) => flag.startsWith('flag_tutor-tank_')) +// // .map(async (flag) => { +// // const content = await readFile( +// // `${TEMP}/${DOI.flags}/${flag}`, +// // { encoding: 'base64' }, +// // ); +// // const name = flag.match(/flag_tutor-tank_(.+)\.packed\.webp/)![1]; + +// // return { +// // content, +// // encoding: 'base64', +// // path: `flags/scratched/${name}.webp`, +// // } satisfies FileChange; +// // }), +// // ); +// const circleFlags = await Promise.all( +// flags +// .filter((flag) => flag.startsWith('flag_profile-stat_')) +// .map(async (flag) => { +// const content = await readFile( +// `${TEMP}/${DOI.flags}/${flag}`, +// { encoding: 'base64' }, +// ); +// const name = flag.match(/flag_profile-stat_(.+)\.packed\.webp/)![1]; + +// return { +// content, +// encoding: 'base64', +// path: `flags/circle/${name}.webp`, +// } satisfies FileChange; +// }), +// ); + +// console.log('Writing files...'); +// await commitMultipleFiles( +// octokit, +// 'tresabhi', +// 'blitzkrieg-assets', +// 'main', +// new Date().toString(), +// circleFlags, +// true, +// ); +// }); diff --git a/scripts/buildRatingsLeaderboard.ts b/scripts/buildRatingsLeaderboard.ts index b69df4cc1..dbe09a0d7 100644 --- a/scripts/buildRatingsLeaderboard.ts +++ b/scripts/buildRatingsLeaderboard.ts @@ -6,8 +6,8 @@ import { RatingsNeighbors, RatingsPlayer, } from '../src/commands/ratings'; +import commitMultipleFiles from '../src/core/blitzkrieg/commitMultipleFiles.js'; import { patientFetchJSON } from '../src/core/blitzkrieg/patientFetchJSON'; -import commitMultipleFiles from './commitMultipleFiles.js'; /* * Central North American Time (UTC-5): use 0 5 * * *. diff --git a/src/components/Tanks/components/Item.tsx b/src/components/Tanks/components/Item.tsx index d7de2cc98..4363e0a80 100644 --- a/src/components/Tanks/components/Item.tsx +++ b/src/components/Tanks/components/Item.tsx @@ -12,10 +12,10 @@ export enum TreeTypeEnum { } export const TANK_TYPES = [ - 'AT-SPG', - 'lightTank', - 'mediumTank', - 'heavyTank', + 'tank_destroyer', + 'light', + 'medium', + 'heavy', ] as const; // TODO: remove hyphen for underscore diff --git a/src/core/blitz/readStringDVPL.ts b/src/core/blitz/readStringDVPL.ts new file mode 100644 index 000000000..d55639d44 --- /dev/null +++ b/src/core/blitz/readStringDVPL.ts @@ -0,0 +1,6 @@ +import { readDVPL } from './readDVPL'; + +export async function readStringDVPL(file: string) { + const buffer = await readDVPL(file); + return buffer.toString(); +} diff --git a/src/core/blitz/readXMLDVPL.ts b/src/core/blitz/readXMLDVPL.ts new file mode 100644 index 000000000..b40031619 --- /dev/null +++ b/src/core/blitz/readXMLDVPL.ts @@ -0,0 +1,9 @@ +import { XMLParser } from 'fast-xml-parser'; +import { readStringDVPL } from './readStringDVPL'; + +export async function readXMLDVPL(file: string) { + const parser = new XMLParser(); + const xml = await readStringDVPL(file); + + return parser.parse(xml).root as Type; +} diff --git a/src/core/blitz/readYAMLDVPL.ts b/src/core/blitz/readYAMLDVPL.ts new file mode 100644 index 000000000..c95623225 --- /dev/null +++ b/src/core/blitz/readYAMLDVPL.ts @@ -0,0 +1,8 @@ +import { parse } from 'yaml'; +import { readStringDVPL } from './readStringDVPL'; + +export async function readYAMLDVPL(file: string) { + const yaml = await readStringDVPL(file); + + return parse(yaml) as Type; +} diff --git a/scripts/commitMultipleFiles.ts b/src/core/blitzkrieg/commitMultipleFiles.ts similarity index 87% rename from scripts/commitMultipleFiles.ts rename to src/core/blitzkrieg/commitMultipleFiles.ts index d24eb2a42..292414229 100644 --- a/scripts/commitMultipleFiles.ts +++ b/src/core/blitzkrieg/commitMultipleFiles.ts @@ -15,9 +15,8 @@ export default async function commitMultipleFiles( branch: string, message: string, changes: FileChange[], - logBlobCreation = false, + verbose = false, ) { - // Get the latest commit SHA on the specified branch const latestCommitSha = ( await octokit.git.getRef({ owner, @@ -25,8 +24,6 @@ export default async function commitMultipleFiles( ref: `heads/${branch}`, }) ).data.object.sha; - - // Get the tree SHA for the latest commit const treeSha = ( await octokit.git.getCommit({ owner, @@ -34,7 +31,6 @@ export default async function commitMultipleFiles( commit_sha: latestCommitSha, }) ).data.tree.sha; - const blobs: { sha: string; path: string; @@ -65,13 +61,14 @@ export default async function commitMultipleFiles( `Failed blob ${changeIndex + 1} / ${ changes.length }; retrying in ${TIME_PER_BLOB}ms...`, + error, ); await new Promise((resolve) => setTimeout(resolve, TIME_PER_BLOB)); } } - if (logBlobCreation) { + if (verbose) { console.log( `Created blob ${changeIndex + 1} / ${changes.length} (${( (100 * (changeIndex + 1)) / @@ -81,15 +78,12 @@ export default async function commitMultipleFiles( } } - // Create a new tree with the new blobs const { data: treeData } = await octokit.git.createTree({ owner, repo, base_tree: treeSha, tree: blobs, }); - - // Create a new commit with the new tree const { data: newCommitData } = await octokit.git.createCommit({ owner, repo, @@ -97,8 +91,6 @@ export default async function commitMultipleFiles( tree: treeData.sha, parents: [latestCommitSha], }); - - // Update the branch reference to point to the new commit await octokit.git.updateRef({ owner, repo, diff --git a/src/core/blitzkrieg/definitions/tanks.ts b/src/core/blitzkrieg/definitions/tanks.ts index f691489a5..224a7424a 100644 --- a/src/core/blitzkrieg/definitions/tanks.ts +++ b/src/core/blitzkrieg/definitions/tanks.ts @@ -47,24 +47,24 @@ export const tankNamesDiacritics = tankNames.then((tankNames) => ); export const TANK_ICONS: Record = { - 'AT-SPG': 'https://i.imgur.com/BIHSEH0.png', - lightTank: 'https://i.imgur.com/CSNha5V.png', - mediumTank: 'https://i.imgur.com/wvf3ltm.png', - heavyTank: 'https://i.imgur.com/ECeqlZa.png', + tank_destroyer: 'https://i.imgur.com/BIHSEH0.png', + light: 'https://i.imgur.com/CSNha5V.png', + medium: 'https://i.imgur.com/wvf3ltm.png', + heavy: 'https://i.imgur.com/ECeqlZa.png', }; export const TANK_ICONS_PREMIUM: Record = { - 'AT-SPG': 'https://i.imgur.com/TCu3EdR.png', - lightTank: 'https://i.imgur.com/zdkpTRb.png', - mediumTank: 'https://i.imgur.com/3z7eHX6.png', - heavyTank: 'https://i.imgur.com/P3vbmyA.png', + tank_destroyer: 'https://i.imgur.com/TCu3EdR.png', + light: 'https://i.imgur.com/zdkpTRb.png', + medium: 'https://i.imgur.com/3z7eHX6.png', + heavy: 'https://i.imgur.com/P3vbmyA.png', }; export const TANK_ICONS_COLLECTOR: Record = { - 'AT-SPG': 'https://i.imgur.com/WTjeirB.png', - lightTank: 'https://i.imgur.com/EwhtKkU.png', - mediumTank: 'https://i.imgur.com/u8YDMBh.png', - heavyTank: 'https://i.imgur.com/8xRf3nc.png', + tank_destroyer: 'https://i.imgur.com/WTjeirB.png', + light: 'https://i.imgur.com/EwhtKkU.png', + medium: 'https://i.imgur.com/u8YDMBh.png', + heavy: 'https://i.imgur.com/8xRf3nc.png', }; export const TIERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const; diff --git a/tsconfig.json b/tsconfig.json index ee57cddeb..6c4176c22 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,6 @@ } ] }, - "include": ["src/**/*", ".next/types/**/*.ts", "dist/website/types/**/*.ts"], + "include": ["src/**/*", "scripts/**/*"], "exclude": ["node_modules", "dist"] } diff --git a/yarn.lock b/yarn.lock index 74b22a608..5a2b22a56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3266,6 +3266,13 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-xml-parser@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz#761e641260706d6e13251c4ef8e3f5694d4b0d79" + integrity sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -4755,11 +4762,6 @@ satori@^0.10.8: postcss-value-parser "^4.2.0" yoga-wasm-web "^0.3.3" -sax@^1.2.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" - integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== - scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -5010,6 +5012,11 @@ strip-json-comments@5.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.1.tgz#0d8b7d01b23848ed7dbdf4baaaa31a8250d8cfa0" integrity sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw== +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" @@ -5482,13 +5489,6 @@ ws@8.14.2, ws@^8.14.2: resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== -xml-js@^1.6.11: - version "1.6.11" - resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" - integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== - dependencies: - sax "^1.2.4" - xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"