diff --git a/.pnp.cjs b/.pnp.cjs index 9302abf177f..db84159d946 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -6600,23 +6600,29 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@fern-api/fdr-sdk", "npm:0.94.3-b6c3996ce"],\ ["@fern-api/fs-utils", "workspace:packages/commons/fs-utils"],\ ["@fern-api/ir-generator", "workspace:packages/cli/generation/ir-generator"],\ + ["@fern-api/logger", "workspace:packages/cli/logger"],\ ["@fern-api/register", "workspace:packages/cli/register"],\ ["@fern-api/task-context", "workspace:packages/cli/task-context"],\ ["@fern-api/workspace-loader", "workspace:packages/cli/workspace-loader"],\ ["@types/cors", "npm:2.8.17"],\ + ["@types/decompress", "npm:4.2.7"],\ ["@types/express", "npm:4.17.20"],\ ["@types/jest", "npm:29.0.3"],\ ["@types/node", "npm:18.7.18"],\ ["@types/uuid", "npm:9.0.8"],\ + ["@types/xml2js", "npm:0.4.14"],\ ["cors", "npm:2.8.5"],\ + ["decompress", "npm:4.2.1"],\ ["depcheck", "npm:1.4.6"],\ ["eslint", "npm:8.56.0"],\ ["express", "npm:4.19.2"],\ ["jest", "virtual:816fb67d993b0978271f762d4ccbec7209ef2546c234ca6e241662d44336c8e32c1c3c07189cfe14b67974a4840e1ed140408a7403bf9deb68c1953445072efe#npm:29.7.0"],\ ["organize-imports-cli", "npm:0.10.0"],\ ["prettier", "npm:2.7.1"],\ + ["tmp-promise", "npm:3.0.3"],\ ["typescript", "patch:typescript@npm%3A4.6.4#~builtin::version=4.6.4&hash=5d3a66"],\ - ["uuid", "npm:9.0.1"]\ + ["uuid", "npm:9.0.1"],\ + ["xml2js", "npm:0.6.2"]\ ],\ "linkType": "SOFT"\ }]\ @@ -11219,6 +11225,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/xml2js", [\ + ["npm:0.4.14", {\ + "packageLocation": "./.yarn/cache/@types-xml2js-npm-0.4.14-595cc9c3cf-df9f106b99.zip/node_modules/@types/xml2js/",\ + "packageDependencies": [\ + ["@types/xml2js", "npm:0.4.14"],\ + ["@types/node", "npm:18.7.18"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/yargs", [\ ["npm:17.0.12", {\ "packageLocation": "./.yarn/cache/@types-yargs-npm-17.0.12-6f235aa456-5b41d21d86.zip/node_modules/@types/yargs/",\ @@ -26215,6 +26231,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["sax", [\ + ["npm:1.3.0", {\ + "packageLocation": "./.yarn/cache/sax-npm-1.3.0-e6c479267f-238ab3a9ba.zip/node_modules/sax/",\ + "packageDependencies": [\ + ["sax", "npm:1.3.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["saxes", [\ ["npm:6.0.0", {\ "packageLocation": "./.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip/node_modules/saxes/",\ @@ -29869,6 +29894,26 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["xml2js", [\ + ["npm:0.6.2", {\ + "packageLocation": "./.yarn/cache/xml2js-npm-0.6.2-64cd781d74-458a838061.zip/node_modules/xml2js/",\ + "packageDependencies": [\ + ["xml2js", "npm:0.6.2"],\ + ["sax", "npm:1.3.0"],\ + ["xmlbuilder", "npm:11.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["xmlbuilder", [\ + ["npm:11.0.1", {\ + "packageLocation": "./.yarn/cache/xmlbuilder-npm-11.0.1-b8b04dc929-7152695e16.zip/node_modules/xmlbuilder/",\ + "packageDependencies": [\ + ["xmlbuilder", "npm:11.0.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["xmlchars", [\ ["npm:2.2.0", {\ "packageLocation": "./.yarn/cache/xmlchars-npm-2.2.0-8b78f0f5e4-8c70ac9407.zip/node_modules/xmlchars/",\ diff --git a/.yarn/cache/@types-xml2js-npm-0.4.14-595cc9c3cf-df9f106b99.zip b/.yarn/cache/@types-xml2js-npm-0.4.14-595cc9c3cf-df9f106b99.zip new file mode 100644 index 00000000000..d6fdcc1ce9b Binary files /dev/null and b/.yarn/cache/@types-xml2js-npm-0.4.14-595cc9c3cf-df9f106b99.zip differ diff --git a/.yarn/cache/sax-npm-1.3.0-e6c479267f-238ab3a9ba.zip b/.yarn/cache/sax-npm-1.3.0-e6c479267f-238ab3a9ba.zip new file mode 100644 index 00000000000..a859ed35342 Binary files /dev/null and b/.yarn/cache/sax-npm-1.3.0-e6c479267f-238ab3a9ba.zip differ diff --git a/.yarn/cache/xml2js-npm-0.6.2-64cd781d74-458a838061.zip b/.yarn/cache/xml2js-npm-0.6.2-64cd781d74-458a838061.zip new file mode 100644 index 00000000000..f264eda13a4 Binary files /dev/null and b/.yarn/cache/xml2js-npm-0.6.2-64cd781d74-458a838061.zip differ diff --git a/.yarn/cache/xmlbuilder-npm-11.0.1-b8b04dc929-7152695e16.zip b/.yarn/cache/xmlbuilder-npm-11.0.1-b8b04dc929-7152695e16.zip new file mode 100644 index 00000000000..b7a7d790900 Binary files /dev/null and b/.yarn/cache/xmlbuilder-npm-11.0.1-b8b04dc929-7152695e16.zip differ diff --git a/packages/cli/cli/.env-cmdrc.cjs b/packages/cli/cli/.env-cmdrc.cjs index 074bd06360b..b666261354a 100644 --- a/packages/cli/cli/.env-cmdrc.cjs +++ b/packages/cli/cli/.env-cmdrc.cjs @@ -9,6 +9,7 @@ module.exports = { LOCAL_STORAGE_FOLDER: ".fern", POSTHOG_API_KEY: process.env.POSTHOG_API_KEY, DOCS_DOMAIN_SUFFIX: "docs.buildwithfern.com", + DOCS_PREVIEW_BUCKET: 'https://prod-local-preview-bundle2.s3.amazonaws.com/' }, dev: { AUTH0_DOMAIN: "fern-dev.us.auth0.com", @@ -20,6 +21,7 @@ module.exports = { LOCAL_STORAGE_FOLDER: ".fern-dev", POSTHOG_API_KEY: null, DOCS_DOMAIN_SUFFIX: "dev.docs.buildwithfern.com", + DOCS_PREVIEW_BUCKET: 'https://dev2-local-preview-bundle2.s3.amazonaws.com/' }, local: { AUTH0_DOMAIN: "fern-dev.us.auth0.com", @@ -31,5 +33,6 @@ module.exports = { LOCAL_STORAGE_FOLDER: ".fern-local", POSTHOG_API_KEY: null, DOCS_DOMAIN_SUFFIX: "dev.docs.buildwithfern.com", + DOCS_PREVIEW_BUCKET: 'https://dev2-local-preview-bundle2.s3.amazonaws.com/' }, }; diff --git a/packages/cli/cli/.mrlint.json b/packages/cli/cli/.mrlint.json index b4de15870f5..f874e41ca5b 100644 --- a/packages/cli/cli/.mrlint.json +++ b/packages/cli/cli/.mrlint.json @@ -25,7 +25,8 @@ "VENUS_AUDIENCE", "LOCAL_STORAGE_FOLDER", "POSTHOG_API_KEY", - "DOCS_DOMAIN_SUFFIX" + "DOCS_DOMAIN_SUFFIX", + "DOCS_PREVIEW_BUCKET" ] }, "plugins": { diff --git a/packages/cli/cli/build.dev.cjs b/packages/cli/cli/build.dev.cjs index 5c9e3af4e63..f9aa2415324 100644 --- a/packages/cli/cli/build.dev.cjs +++ b/packages/cli/cli/build.dev.cjs @@ -29,7 +29,8 @@ async function main() { "process.env.VENUS_AUDIENCE": getEnvironmentVariable("VENUS_AUDIENCE"), "process.env.LOCAL_STORAGE_FOLDER": getEnvironmentVariable("LOCAL_STORAGE_FOLDER"), "process.env.POSTHOG_API_KEY": getEnvironmentVariable("POSTHOG_API_KEY"), - "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX") + "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX"), + "process.env.DOCS_PREVIEW_BUCKET": getEnvironmentVariable("DOCS_PREVIEW_BUCKET") } }; diff --git a/packages/cli/cli/build.local.cjs b/packages/cli/cli/build.local.cjs index abb5d89709d..1e7ac37dd80 100644 --- a/packages/cli/cli/build.local.cjs +++ b/packages/cli/cli/build.local.cjs @@ -29,7 +29,8 @@ async function main() { "process.env.VENUS_AUDIENCE": getEnvironmentVariable("VENUS_AUDIENCE"), "process.env.LOCAL_STORAGE_FOLDER": getEnvironmentVariable("LOCAL_STORAGE_FOLDER"), "process.env.POSTHOG_API_KEY": getEnvironmentVariable("POSTHOG_API_KEY"), - "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX") + "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX"), + "process.env.DOCS_PREVIEW_BUCKET": getEnvironmentVariable("DOCS_PREVIEW_BUCKET") } }; diff --git a/packages/cli/cli/build.prod.cjs b/packages/cli/cli/build.prod.cjs index 3dc891e2fb3..d660e46b879 100644 --- a/packages/cli/cli/build.prod.cjs +++ b/packages/cli/cli/build.prod.cjs @@ -29,7 +29,8 @@ async function main() { "process.env.VENUS_AUDIENCE": getEnvironmentVariable("VENUS_AUDIENCE"), "process.env.LOCAL_STORAGE_FOLDER": getEnvironmentVariable("LOCAL_STORAGE_FOLDER"), "process.env.POSTHOG_API_KEY": getEnvironmentVariable("POSTHOG_API_KEY"), - "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX") + "process.env.DOCS_DOMAIN_SUFFIX": getEnvironmentVariable("DOCS_DOMAIN_SUFFIX"), + "process.env.DOCS_PREVIEW_BUCKET": getEnvironmentVariable("DOCS_PREVIEW_BUCKET") } }; diff --git a/packages/cli/cli/src/cli.ts b/packages/cli/cli/src/cli.ts index 26dafeacd1f..dc0d230e454 100644 --- a/packages/cli/cli/src/cli.ts +++ b/packages/cli/cli/src/cli.ts @@ -141,7 +141,7 @@ async function tryRunCli(cliContext: CliContext) { addLoginCommand(cli, cliContext); addFormatCommand(cli, cliContext); addWriteDefinitionCommand(cli, cliContext); - addPreviewCommand(cli, cliContext); + addDocsPreviewCommand(cli, cliContext); addMockCommand(cli, cliContext); addWriteOverridesCommand(cli, cliContext); addTestCommand(cli, cliContext); @@ -882,9 +882,9 @@ function addWriteDefinitionCommand(cli: Argv, cliContext: CliC ); } -function addPreviewCommand(cli: Argv, cliContext: CliContext) { +function addDocsPreviewCommand(cli: Argv, cliContext: CliContext) { cli.command( - "preview", + "docs preview", false, // hide from help message (yargs) => yargs, async () => { diff --git a/packages/cli/docs-preview/package.json b/packages/cli/docs-preview/package.json index a98f3f9f0e0..21485daf442 100644 --- a/packages/cli/docs-preview/package.json +++ b/packages/cli/docs-preview/package.json @@ -32,12 +32,17 @@ "@fern-api/fdr-sdk": "0.94.3-b6c3996ce", "@fern-api/fs-utils": "workspace:*", "@fern-api/ir-generator": "workspace:*", + "@fern-api/logger": "workspace:*", "@fern-api/register": "workspace:*", "@fern-api/task-context": "workspace:*", "@fern-api/workspace-loader": "workspace:*", + "@types/decompress": "^4.2.7", "cors": "^2.8.5", + "decompress": "^4.2.1", "express": "^4.19.2", - "uuid": "^9.0.1" + "tmp-promise": "^3.0.3", + "uuid": "^9.0.1", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/cors": "^2.8.13", @@ -45,6 +50,7 @@ "@types/jest": "^29.0.3", "@types/node": "^18.7.18", "@types/uuid": "^9.0.8", + "@types/xml2js": "^0.4.14", "depcheck": "^1.4.6", "eslint": "^8.56.0", "jest": "^29.7.0", diff --git a/packages/cli/docs-preview/src/__test__/downloadLocalDocsBundle.test.ts b/packages/cli/docs-preview/src/__test__/downloadLocalDocsBundle.test.ts new file mode 100644 index 00000000000..ea2f398f6ba --- /dev/null +++ b/packages/cli/docs-preview/src/__test__/downloadLocalDocsBundle.test.ts @@ -0,0 +1,14 @@ +import { CONSOLE_LOGGER } from "@fern-api/logger"; +import { downloadBundle } from "../downloadLocalDocsBundle"; + +describe("preview", () => { + it("download frontend", async () => { + await downloadBundle({ + bucketUrl: "https://dev2-local-preview-bundle2.s3.amazonaws.com/", + logger: CONSOLE_LOGGER, + preferCached: false + }); + + expect(true).toEqual(true); + }, 60_000); +}); diff --git a/packages/cli/docs-preview/src/downloadLocalDocsBundle.ts b/packages/cli/docs-preview/src/downloadLocalDocsBundle.ts new file mode 100644 index 00000000000..a97b7ffb98f --- /dev/null +++ b/packages/cli/docs-preview/src/downloadLocalDocsBundle.ts @@ -0,0 +1,132 @@ +import { AbsoluteFilePath, doesPathExist, join, RelativeFilePath } from "@fern-api/fs-utils"; +import { Logger } from "@fern-api/logger"; +import decompress from "decompress"; +import { createWriteStream } from "fs"; +import { mkdir, readFile, rm, writeFile } from "fs/promises"; +import { homedir } from "os"; +import path from "path"; +import { pipeline } from "stream/promises"; +import tmp from "tmp-promise"; +import xml2js from "xml2js"; + +const ETAG_FILENAME = "etag"; +const PREVIEW_FOLDER_NAME = "preview"; +const BUNDLE_FOLDER_NAME = "bundle"; +const LOCAL_STORAGE_FOLDER = process.env.LOCAL_STORAGE_FOLDER ?? ".fern"; + +export function getLocalStorageFolder(): AbsoluteFilePath { + return join(AbsoluteFilePath.of(homedir()), RelativeFilePath.of(LOCAL_STORAGE_FOLDER)); +} + +export function getPathToPreviewFolder(): AbsoluteFilePath { + return join( + AbsoluteFilePath.of(homedir()), + RelativeFilePath.of(LOCAL_STORAGE_FOLDER), + RelativeFilePath.of(PREVIEW_FOLDER_NAME) + ); +} + +export function getPathToBundleFolder(): AbsoluteFilePath { + return join( + AbsoluteFilePath.of(homedir()), + RelativeFilePath.of(LOCAL_STORAGE_FOLDER), + RelativeFilePath.of(PREVIEW_FOLDER_NAME), + RelativeFilePath.of(BUNDLE_FOLDER_NAME) + ); +} + +export function getPathToEtagFile(): AbsoluteFilePath { + return join( + AbsoluteFilePath.of(homedir()), + RelativeFilePath.of(LOCAL_STORAGE_FOLDER), + RelativeFilePath.of(PREVIEW_FOLDER_NAME), + RelativeFilePath.of(ETAG_FILENAME) + ); +} + +export declare namespace DownloadLocalBundle { + type Result = SuccesResult | FailureResult; + + interface SuccesResult { + type: "success"; + } + + interface FailureResult { + type: "failure"; + } +} + +export async function downloadBundle({ + bucketUrl, + logger, + preferCached +}: { + bucketUrl: string; + logger: Logger; + preferCached: boolean; +}): Promise { + logger.debug("Setting up docs preview bundle..."); + const response = await fetch(bucketUrl); + const body = await response.text(); + const parser = new xml2js.Parser(); + const parsedResponse = await parser.parseStringPromise(body); + const eTag = parsedResponse?.ListBucketResult?.Contents?.[0]?.ETag?.[0]; + const key = parsedResponse?.ListBucketResult?.Contents?.[0]?.Key?.[0]; + + const eTagFilepath = getPathToEtagFile(); + if (preferCached) { + const currentETagExists = await doesPathExist(eTagFilepath); + let currentETag = undefined; + if (currentETagExists) { + logger.debug("Reading existing ETag"); + currentETag = (await readFile(eTagFilepath)).toString(); + } + if (currentETag != null && currentETag === eTag) { + logger.debug("ETag matches. Using already downloaded bundle"); + // The bundle is already downloaded + return { + type: "success" + }; + } else { + logger.debug("ETag is different. Downloading latest preview bundle"); + } + } + + // create tmp directory + const dir = await tmp.dir({ prefix: "fern" }); + const absoluteDirectoryToTmpDir = AbsoluteFilePath.of(dir.path); + logger.debug(`Created tmp directory ${absoluteDirectoryToTmpDir}`); + + // download docs bundle + const docsBundleZipResponse = await fetch(`${path.join(bucketUrl, key)}`); + const outputZipPath = join(absoluteDirectoryToTmpDir, RelativeFilePath.of("output.zip")); + + const contents = docsBundleZipResponse.body; + if (contents == null) { + return { + type: "failure" + }; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await pipeline(contents as any, createWriteStream(outputZipPath)); + logger.debug(`Wrote output.zip to ${outputZipPath}`); + + const absolutePathToPreviewFolder = getPathToPreviewFolder(); + if (await doesPathExist(absolutePathToPreviewFolder)) { + await rm(absolutePathToPreviewFolder, { recursive: true }); + } + await mkdir(absolutePathToPreviewFolder, { recursive: true }); + logger.debug(`rm -rf ${absolutePathToPreviewFolder}`); + + const absolutePathToBundleFolder = getPathToBundleFolder(); + await mkdir(absolutePathToBundleFolder, { recursive: true }); + await decompress(outputZipPath, absolutePathToBundleFolder); + + // write etag + await writeFile(eTagFilepath, eTag); + logger.debug(`Downloaded bundle to ${absolutePathToBundleFolder}`); + + return { + type: "success" + }; +} diff --git a/packages/cli/docs-preview/src/runPreviewServer.ts b/packages/cli/docs-preview/src/runPreviewServer.ts index c73980bea92..5645f5ab272 100644 --- a/packages/cli/docs-preview/src/runPreviewServer.ts +++ b/packages/cli/docs-preview/src/runPreviewServer.ts @@ -3,6 +3,8 @@ import { TaskContext } from "@fern-api/task-context"; import { APIWorkspace, DocsWorkspace } from "@fern-api/workspace-loader"; import cors from "cors"; import express from "express"; +import path from "path"; +import { downloadBundle, getPathToBundleFolder } from "./downloadLocalDocsBundle"; import { getPreviewDocsDefinition } from "./previewDocs"; export async function runPreviewServer({ @@ -14,6 +16,14 @@ export async function runPreviewServer({ apiWorkspaces: APIWorkspace[]; context: TaskContext; }): Promise { + const url = process.env.DOCS_PREVIEW_BUCKET; + if (url == null) { + context.failAndThrow("Failed to connect to the docs preview server. Please contact support@buildwithfern.com"); + return; + } + + await downloadBundle({ bucketUrl: url, logger: context.logger, preferCached: true }); + const app = express(); app.use(cors()); @@ -36,7 +46,13 @@ export async function runPreviewServer({ }); app.listen(3000); - context.logger.info("Running server on https://localhost:3000"); + app.use("/_next", express.static(path.join(getPathToBundleFolder(), "/_next"))); + + app.use("*", async (_req, res) => { + return res.sendFile(path.join(getPathToBundleFolder(), "/[[...slug]].html")); + }); + + context.logger.info("Running server on http://localhost:3000"); // await infiinitely // eslint-disable-next-line @typescript-eslint/no-empty-function diff --git a/packages/cli/docs-preview/tsconfig.json b/packages/cli/docs-preview/tsconfig.json index 00257b9c645..64ed532b4e6 100644 --- a/packages/cli/docs-preview/tsconfig.json +++ b/packages/cli/docs-preview/tsconfig.json @@ -7,6 +7,7 @@ { "path": "../../commons/fs-utils" }, { "path": "../configuration" }, { "path": "../generation/ir-generator" }, + { "path": "../logger" }, { "path": "../register" }, { "path": "../task-context" }, { "path": "../workspace-loader" } diff --git a/packages/cli/ete-tests/src/tests/preview/preview.test.ts b/packages/cli/ete-tests/src/tests/preview/preview.test.ts index 6ad4d7ffc4f..a3e79008874 100644 --- a/packages/cli/ete-tests/src/tests/preview/preview.test.ts +++ b/packages/cli/ete-tests/src/tests/preview/preview.test.ts @@ -6,11 +6,11 @@ const fixturesDir = join(AbsoluteFilePath.of(__dirname), RelativeFilePath.of("fi describe("fern preview", () => { it("preview basic docs", async () => { - void runFernCli(["preview"], { + void runFernCli(["docs", "preview"], { cwd: join(fixturesDir, RelativeFilePath.of("simple")) }); - await sleep(5000); + await sleep(20_000); const response = await fetch("http://localhost:3000/v2/registry/docs/load-with-url", { method: "POST" diff --git a/yarn.lock b/yarn.lock index 2d968845acc..dee8088b616 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3561,23 +3561,29 @@ __metadata: "@fern-api/fdr-sdk": 0.94.3-b6c3996ce "@fern-api/fs-utils": "workspace:*" "@fern-api/ir-generator": "workspace:*" + "@fern-api/logger": "workspace:*" "@fern-api/register": "workspace:*" "@fern-api/task-context": "workspace:*" "@fern-api/workspace-loader": "workspace:*" "@types/cors": ^2.8.13 + "@types/decompress": ^4.2.7 "@types/express": ^4.17.20 "@types/jest": ^29.0.3 "@types/node": ^18.7.18 "@types/uuid": ^9.0.8 + "@types/xml2js": ^0.4.14 cors: ^2.8.5 + decompress: ^4.2.1 depcheck: ^1.4.6 eslint: ^8.56.0 express: ^4.19.2 jest: ^29.7.0 organize-imports-cli: ^0.10.0 prettier: ^2.7.1 + tmp-promise: ^3.0.3 typescript: 4.6.4 uuid: ^9.0.1 + xml2js: ^0.6.2 languageName: unknown linkType: soft @@ -7701,6 +7707,15 @@ __metadata: languageName: node linkType: hard +"@types/xml2js@npm:^0.4.14": + version: 0.4.14 + resolution: "@types/xml2js@npm:0.4.14" + dependencies: + "@types/node": "*" + checksum: df9f106b9953dcdec7ba3304ebc56d6c2f61d49bf556d600bed439f94a1733f73ca0bf2d0f64330b402191622862d9d6058bab9d7e3dcb5b0fe51ebdc4372aac + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -21132,6 +21147,13 @@ env-cmd@toddbluhm/env-cmd: languageName: node linkType: hard +"sax@npm:>=0.6.0": + version: 1.3.0 + resolution: "sax@npm:1.3.0" + checksum: 238ab3a9ba8c8f8aaf1c5ea9120386391f6ee0af52f1a6a40bbb6df78241dd05d782f2359d614ac6aae08c4c4125208b456548a6cf68625aa4fe178486e63ecd + languageName: node + linkType: hard + "saxes@npm:^6.0.0": version: 6.0.0 resolution: "saxes@npm:6.0.0" @@ -24366,6 +24388,23 @@ env-cmd@toddbluhm/env-cmd: languageName: node linkType: hard +"xml2js@npm:^0.6.2": + version: 0.6.2 + resolution: "xml2js@npm:0.6.2" + dependencies: + sax: ">=0.6.0" + xmlbuilder: ~11.0.0 + checksum: 458a83806193008edff44562c0bdb982801d61ee7867ae58fd35fab781e69e17f40dfeb8fc05391a4648c9c54012066d3955fe5d993ffbe4dc63399023f32ac2 + languageName: node + linkType: hard + +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0 + languageName: node + linkType: hard + "xmlchars@npm:^2.2.0": version: 2.2.0 resolution: "xmlchars@npm:2.2.0"