diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2e925aeab..606a88f6b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,10 +42,10 @@ jobs: run: yarn run tsc - name: build docker run: docker-compose build - - name: run jest + - name: run all tests as root run: sudo yarn test - - name: run saved state test with volume owned by different user + - name: run saved state + qa compare test as non-root - with volume owned by current user run: | sudo rm -rf ./test-crawls mkdir test-crawls - sudo yarn test ./tests/saved-state.test.js + sudo yarn test ./tests/saved-state.test.js ./tests/qa_compare.test.js diff --git a/Dockerfile b/Dockerfile index 57b319b54..4502be2e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,9 +48,15 @@ ADD config/ /app/ ADD html/ /app/html/ -RUN chmod a+x /app/dist/main.js /app/dist/create-login-profile.js +ARG RWP_VERSION=1.8.15 +ADD https://cdn.jsdelivr.net/npm/replaywebpage@${RWP_VERSION}/ui.js /app/html/rwp/ +ADD https://cdn.jsdelivr.net/npm/replaywebpage@${RWP_VERSION}/sw.js /app/html/rwp/ -RUN ln -s /app/dist/main.js /usr/bin/crawl; ln -s /app/dist/create-login-profile.js /usr/bin/create-login-profile +RUN chmod a+x /app/dist/main.js /app/dist/create-login-profile.js && chmod a+r /app/html/rwp/* + +RUN ln -s /app/dist/main.js /usr/bin/crawl; \ + ln -s /app/dist/main.js /usr/bin/qa; \ + ln -s /app/dist/create-login-profile.js /usr/bin/create-login-profile WORKDIR /crawls diff --git a/html/replay.html b/html/replay.html new file mode 100644 index 000000000..2013b13e2 --- /dev/null +++ b/html/replay.html @@ -0,0 +1,39 @@ + + + + + + + + + + + diff --git a/package.json b/package.json index 6f6cc5771..883f7560b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browsertrix-crawler", - "version": "1.0.2", + "version": "1.1.0-beta.2", "main": "browsertrix-crawler", "type": "module", "repository": "https://github.com/webrecorder/browsertrix-crawler", @@ -24,9 +24,12 @@ "get-folder-size": "^4.0.0", "husky": "^8.0.3", "ioredis": "^5.3.2", + "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "minio": "^7.1.3", "p-queue": "^7.3.4", + "pixelmatch": "^5.3.0", + "pngjs": "^7.0.0", "puppeteer-core": "^20.8.2", "sax": "^1.3.0", "sharp": "^0.32.6", @@ -37,8 +40,11 @@ "yargs": "^17.7.2" }, "devDependencies": { + "@types/js-levenshtein": "^1.1.3", "@types/js-yaml": "^4.0.8", "@types/node": "^20.8.7", + "@types/pixelmatch": "^5.2.6", + "@types/pngjs": "^6.0.4", "@types/uuid": "^9.0.6", "@types/ws": "^8.5.8", "@typescript-eslint/eslint-plugin": "^6.10.0", @@ -46,7 +52,7 @@ "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-react": "^7.22.0", - "jest": "^29.2.1", + "jest": "^29.7.0", "md5": "^2.3.0", "prettier": "3.0.3", "typescript": "^5.2.2" diff --git a/src/crawler.ts b/src/crawler.ts index e0289c078..5ae57fa16 100644 --- a/src/crawler.ts +++ b/src/crawler.ts @@ -13,6 +13,8 @@ import { PageCallbacks, } from "./util/state.js"; +import { parseArgs } from "./util/argParser.js"; + import yaml from "js-yaml"; import * as warcio from "warcio"; @@ -29,7 +31,6 @@ import { } from "./util/storage.js"; import { ScreenCaster, WSTransport } from "./util/screencaster.js"; import { Screenshots } from "./util/screenshots.js"; -import { parseArgs } from "./util/argParser.js"; import { initRedis } from "./util/redis.js"; import { logger, formatErr } from "./util/logger.js"; import { @@ -57,6 +58,7 @@ import { OriginOverride } from "./util/originoverride.js"; import { Agent as HTTPAgent } from "http"; import { Agent as HTTPSAgent } from "https"; import { CDPSession, Frame, HTTPRequest, Page } from "puppeteer-core"; +import { Recorder } from "./util/recorder.js"; import { SitemapReader } from "./util/sitemapper.js"; import { ScopedSeed } from "./util/seeds.js"; @@ -146,6 +148,8 @@ export class Crawler { pagesDir: string; pagesFile: string; + archivesDir: string; + blockRules: BlockRules | null; adBlockRules: AdBlockRules | null; @@ -154,11 +158,15 @@ export class Crawler { screencaster: ScreenCaster | null = null; + skipTextDocs = 0; + interrupted = false; finalExit = false; uploadAndDeleteLocal = false; done = false; + textInPages = false; + customBehaviors = ""; behaviorsChecked = false; behaviorLastLine?: string; @@ -178,10 +186,12 @@ export class Crawler { crawler: Crawler; }) => NonNullable; + recording = true; + constructor() { - const res = parseArgs(); - this.params = res.parsed; - this.origConfig = res.origConfig; + const args = this.parseArgs(); + this.params = args.parsed; + this.origConfig = args.origConfig; // root collections dir this.collDir = path.join( @@ -259,6 +269,9 @@ export class Crawler { // pages file this.pagesFile = path.join(this.pagesDir, "pages.jsonl"); + // archives dir + this.archivesDir = path.join(this.collDir, "archive"); + this.blockRules = null; this.adBlockRules = null; @@ -268,6 +281,8 @@ export class Crawler { this.finalExit = false; this.uploadAndDeleteLocal = false; + this.textInPages = this.params.text.includes("to-pages"); + this.done = false; this.customBehaviors = ""; @@ -281,6 +296,10 @@ export class Crawler { } } + protected parseArgs() { + return parseArgs(); + } + configureUA() { // override userAgent if (this.params.userAgent) { @@ -434,7 +453,9 @@ export class Crawler { // logger.info("wb-manager init failed, collection likely already exists"); //} - fs.mkdirSync(this.logDir, { recursive: true }); + await fsp.mkdir(this.logDir, { recursive: true }); + await fsp.mkdir(this.archivesDir, { recursive: true }); + this.logFH = fs.createWriteStream(this.logFilename); logger.setExternalLogStream(this.logFH); @@ -721,10 +742,10 @@ self.__bx_behaviors.selectMainBehavior(); return ""; } - async crawlPage(opts: WorkerState) { + async crawlPage(opts: WorkerState): Promise { await this.writeStats(); - const { page, cdp, data, workerid, callbacks, directFetchCapture } = opts; + const { page, data, workerid, callbacks, directFetchCapture } = opts; data.callbacks = callbacks; const { url } = data; @@ -764,7 +785,7 @@ self.__bx_behaviors.selectMainBehavior(); { url, ...logDetails }, "fetch", ); - return true; + return; } } catch (e) { // filtered out direct fetch @@ -782,7 +803,14 @@ self.__bx_behaviors.selectMainBehavior(); data.title = await page.title(); data.favicon = await this.getFavicon(page, logDetails); - const archiveDir = path.join(this.collDir, "archive"); + await this.doPostLoadActions(opts); + } + + async doPostLoadActions(opts: WorkerState, saveOutput = false) { + const { page, cdp, data, workerid } = opts; + const { url } = data; + + const logDetails = { page: url, workerid }; if (this.params.screenshot) { if (!data.isHTMLPage) { @@ -793,10 +821,10 @@ self.__bx_behaviors.selectMainBehavior(); browser: this.browser, page, url, - directory: archiveDir, + directory: this.archivesDir, }); if (this.params.screenshot.includes("view")) { - await screenshots.take(); + await screenshots.take("view", saveOutput ? data : null); } if (this.params.screenshot.includes("fullPage")) { await screenshots.takeFullPage(); @@ -812,15 +840,16 @@ self.__bx_behaviors.selectMainBehavior(); textextract = new TextExtractViaSnapshot(cdp, { warcPrefix: this.warcPrefix, url, - directory: archiveDir, + directory: this.archivesDir, + skipDocs: this.skipTextDocs, }); - const { changed, text } = await textextract.extractAndStoreText( + const { text } = await textextract.extractAndStoreText( "text", false, this.params.text.includes("to-warc"), ); - if (changed && text && this.params.text.includes("to-pages")) { + if (text && (this.textInPages || saveOutput)) { data.text = text; } } @@ -868,8 +897,6 @@ self.__bx_behaviors.selectMainBehavior(); ); await sleep(this.params.pageExtraDelay); } - - return true; } async pageFinished(data: PageState) { @@ -1047,8 +1074,7 @@ self.__bx_behaviors.selectMainBehavior(); async checkLimits() { let interrupt = false; - const dir = path.join(this.collDir, "archive"); - const size = await getDirSize(dir); + const size = await getDirSize(this.archivesDir); await this.crawlState.setArchiveSize(size); @@ -1230,28 +1256,11 @@ self.__bx_behaviors.selectMainBehavior(); this.screencaster = this.initScreenCaster(); - if (this.params.originOverride.length) { + if (this.params.originOverride && this.params.originOverride.length) { this.originOverride = new OriginOverride(this.params.originOverride); } - for (let i = 0; i < this.params.scopedSeeds.length; i++) { - const seed = this.params.scopedSeeds[i]; - if (!(await this.queueUrl(i, seed.url, 0, 0))) { - if (this.limitHit) { - break; - } - } - - if (seed.sitemap) { - await timedRun( - this.parseSitemap(seed, i), - SITEMAP_INITIAL_FETCH_TIMEOUT_SECS, - "Sitemap initial fetch timed out", - { sitemap: seed.sitemap, seed: seed.url }, - "sitemap", - ); - } - } + await this._addInitialSeeds(); await this.browser.launch({ profileUrl: this.params.profile, @@ -1272,12 +1281,14 @@ self.__bx_behaviors.selectMainBehavior(); "browser", ); }, + + recording: this.recording, // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); // -------------- // Run Crawl Here! - await runWorkers(this, this.params.workers, this.maxPageTime, this.collDir); + await runWorkers(this, this.params.workers, this.maxPageTime); // -------------- await this.serializeConfig(true); @@ -1297,6 +1308,27 @@ self.__bx_behaviors.selectMainBehavior(); await this.postCrawl(); } + protected async _addInitialSeeds() { + for (let i = 0; i < this.params.scopedSeeds.length; i++) { + const seed = this.params.scopedSeeds[i]; + if (!(await this.queueUrl(i, seed.url, 0, 0))) { + if (this.limitHit) { + break; + } + } + + if (seed.sitemap) { + await timedRun( + this.parseSitemap(seed, i), + SITEMAP_INITIAL_FETCH_TIMEOUT_SECS, + "Sitemap initial fetch timed out", + { sitemap: seed.sitemap, seed: seed.url }, + "sitemap", + ); + } + } + } + async postCrawl() { if (this.params.combineWARC) { await this.combineWARC(); @@ -1307,9 +1339,9 @@ self.__bx_behaviors.selectMainBehavior(); await fsp.mkdir(path.join(this.collDir, "indexes"), { recursive: true }); await this.crawlState.setStatus("generate-cdx"); - const warcList = await fsp.readdir(path.join(this.collDir, "archive")); + const warcList = await fsp.readdir(this.archivesDir); const warcListFull = warcList.map((filename) => - path.join(this.collDir, "archive", filename), + path.join(this.archivesDir, filename), ); //const indexResult = await this.awaitProcess(child_process.spawn("wb-manager", ["reindex", this.params.collection], {cwd: this.params.cwd})); @@ -1377,10 +1409,8 @@ self.__bx_behaviors.selectMainBehavior(); logger.info("Generating WACZ"); await this.crawlState.setStatus("generate-wacz"); - const archiveDir = path.join(this.collDir, "archive"); - // Get a list of the warcs inside - const warcFileList = await fsp.readdir(archiveDir); + const warcFileList = await fsp.readdir(this.archivesDir); // is finished (>0 pages and all pages written) const isFinished = await this.crawlState.isFinished(); @@ -1440,7 +1470,9 @@ self.__bx_behaviors.selectMainBehavior(); createArgs.push("-f"); - warcFileList.forEach((val) => createArgs.push(path.join(archiveDir, val))); + warcFileList.forEach((val) => + createArgs.push(path.join(this.archivesDir, val)), + ); // create WACZ const waczResult = await this.awaitProcess( @@ -1900,13 +1932,15 @@ self.__bx_behaviors.selectMainBehavior(); depth: number, extraHops: number, logDetails: LogDetails = {}, + ts = 0, + pageid?: string, ) { if (this.limitHit) { return false; } const result = await this.crawlState.addToQueue( - { url, seedId, depth, extraHops }, + { url, seedId, depth, extraHops, ts, pageid }, this.pageLimit, ); @@ -1954,7 +1988,7 @@ self.__bx_behaviors.selectMainBehavior(); id: "pages", title: "All Pages", }; - header["hasText"] = this.params.text.includes("to-pages"); + header["hasText"] = String(this.textInPages); if (this.params.text.length) { logger.debug("Text Extraction: " + this.params.text.join(",")); } else { @@ -1968,20 +2002,30 @@ self.__bx_behaviors.selectMainBehavior(); } } - async writePage({ - pageid, - url, - depth, - title, - text, - loadState, - mime, - favicon, - ts, - status, - }: PageState) { - const row: PageEntry = { id: pageid!, url, title, loadState }; + protected pageEntryForRedis( + entry: Record, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + state: PageState, + ) { + return entry; + } + + async writePage(state: PageState) { + const { + pageid, + url, + depth, + title, + text, + loadState, + mime, + favicon, + status, + } = state; + + const row: PageEntry = { id: pageid, url, title, loadState }; + let { ts } = state; if (!ts) { ts = new Date(); logger.warn("Page date missing, setting to now", { url, ts }); @@ -1998,14 +2042,16 @@ self.__bx_behaviors.selectMainBehavior(); } if (this.params.writePagesToRedis) { - await this.crawlState.writeToPagesQueue(JSON.stringify(row)); + await this.crawlState.writeToPagesQueue( + JSON.stringify(this.pageEntryForRedis(row, state)), + ); } if (depth === 0) { row.seed = true; } - if (text) { + if (text && this.textInPages) { row.text = text; } @@ -2151,7 +2197,7 @@ self.__bx_behaviors.selectMainBehavior(); await this.crawlState.setStatus("generate-warc"); // Get the list of created Warcs - const warcLists = await fsp.readdir(path.join(this.collDir, "archive")); + const warcLists = await fsp.readdir(this.archivesDir); logger.debug(`Combining ${warcLists.length} WARCs...`); @@ -2159,7 +2205,7 @@ self.__bx_behaviors.selectMainBehavior(); // Go through a list of the created works and create an array sorted by their filesize with the largest file first. for (let i = 0; i < warcLists.length; i++) { - const fileName = path.join(this.collDir, "archive", warcLists[i]); + const fileName = path.join(this.archivesDir, warcLists[i]); const fileSize = await getFileSize(fileName); fileSizeObjects.push({ fileSize: fileSize, fileName: fileName }); fileSizeObjects.sort((a, b) => b.fileSize - a.fileSize); @@ -2316,6 +2362,21 @@ self.__bx_behaviors.selectMainBehavior(); await this.storage.uploadFile(filename, targetFilename); } } + + createRecorder(id: number): Recorder | null { + if (!this.recording) { + return null; + } + + const res = new Recorder({ + workerid: id, + collDir: this.collDir, + crawler: this, + }); + + this.browser.recorders.push(res); + return res; + } } function shouldIgnoreAbort(req: HTTPRequest) { diff --git a/src/create-login-profile.ts b/src/create-login-profile.ts index 923281ff3..1fb110469 100755 --- a/src/create-login-profile.ts +++ b/src/create-login-profile.ts @@ -186,6 +186,7 @@ async function main() { "--test-type", ], }, + recording: false, }); if (params.interactive) { diff --git a/src/main.ts b/src/main.ts index f13e72631..4433c6a9c 100755 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { logger } from "./util/logger.js"; import { setExitOnRedisError } from "./util/redis.js"; import { Crawler } from "./crawler.js"; +import { ReplayCrawler } from "./replaycrawler.js"; let crawler: Crawler | null = null; @@ -49,5 +50,10 @@ process.on("SIGABRT", async () => { forceTerm = true; }); -crawler = new Crawler(); +if (process.argv[1].endsWith("qa")) { + crawler = new ReplayCrawler(); +} else { + crawler = new Crawler(); +} + crawler.run(); diff --git a/src/replaycrawler.ts b/src/replaycrawler.ts new file mode 100644 index 000000000..8ab8b8bfe --- /dev/null +++ b/src/replaycrawler.ts @@ -0,0 +1,731 @@ +import { Page, Protocol } from "puppeteer-core"; +import { Crawler } from "./crawler.js"; +import { ReplayServer } from "./util/replayserver.js"; +import { sleep } from "./util/timing.js"; +import { logger } from "./util/logger.js"; +import { WorkerOpts, WorkerState } from "./util/worker.js"; +import { PageState } from "./util/state.js"; +import { PageInfoRecord, PageInfoValue, Recorder } from "./util/recorder.js"; + +import fsp from "fs/promises"; +import path from "path"; + +// @ts-expect-error wabac.js +import { ZipRangeReader } from "@webrecorder/wabac/src/wacz/ziprangereader.js"; +// @ts-expect-error wabac.js +import { createLoader } from "@webrecorder/wabac/src/blockloaders.js"; + +import { AsyncIterReader } from "warcio"; +import { WARCResourceWriter } from "./util/warcresourcewriter.js"; +import { parseArgs } from "./util/argParser.js"; + +import { PNG } from "pngjs"; +import pixelmatch from "pixelmatch"; + +import levenshtein from "js-levenshtein"; +import { MAX_URL_LENGTH } from "./util/reqresp.js"; +import { openAsBlob } from "fs"; + +// RWP Replay Prefix +const REPLAY_PREFIX = "http://localhost:9990/replay/w/replay/"; + +// RWP Source Url +const REPLAY_SOURCE = "http://localhost:9990/replay/?source="; + +// When iterating over page.frames(), the first two frames are for the top-level page +// and RWP embed, the actual content starts with frame index 2 +const SKIP_FRAMES = 2; + +type ReplayPage = { + url: string; + ts: number; + id: string; +}; + +type ComparisonData = { + comparison: { + screenshotMatch?: number; + textMatch?: number; + resourceCounts: { + crawlGood?: number; + crawlBad?: number; + replayGood?: number; + replayBad?: number; + }; + }; +}; + +type ReplayPageInfoRecord = PageInfoRecord & ComparisonData; + +type ComparisonPageState = PageState & ComparisonData; + +// ============================================================================ +// Crawler designed to run over replay of existing WACZ files to generate comparison +// data (eg. for QA) +export class ReplayCrawler extends Crawler { + replayServer: ReplayServer; + qaSource: string; + + pageInfos: Map; + + reloadTimeouts: WeakMap; + + constructor() { + super(); + this.recording = false; + if (!this.params.qaSource) { + throw new Error("Missing QA source"); + } + this.qaSource = this.params.qaSource; + this.replayServer = new ReplayServer(this.qaSource); + + logger.info( + "Replay Crawl with Source", + { source: this.qaSource }, + "general", + ); + + this.pageInfos = new Map(); + + // skip text from first two frames, as they are RWP boilerplate + this.skipTextDocs = SKIP_FRAMES; + + this.params.scopedSeeds = []; + + this.params.screenshot = ["view"]; + this.params.text = ["to-warc"]; + + this.params.serviceWorker = "enabled"; + + this.reloadTimeouts = new WeakMap(); + } + + protected parseArgs() { + return parseArgs(process.argv, true); + } + + async setupPage(opts: WorkerState) { + await super.setupPage(opts); + const { page, cdp } = opts; + + if (!this.qaSource) { + throw new Error("Missing QA source"); + } + + await cdp.send("Network.enable"); + + cdp.on("Network.responseReceived", async (params) => + this.handlePageResourceResponse(params, page), + ); + + cdp.on("Network.requestWillBeSent", (params) => + this.handleRequestWillBeSent(params, page), + ); + + await page.goto(this.replayServer.homePage); + + // wait until content frame is available + while (page.frames().length < SKIP_FRAMES) { + await sleep(5); + } + + const frame = page.frames()[1]; + + await frame.evaluate(() => { + return navigator.serviceWorker.ready; + }); + } + + protected async _addInitialSeeds() { + await this.loadPages(this.qaSource); + } + + isInScope() { + return true; + } + + async loadPages(url: string) { + let path = url; + + try { + path = new URL(url).pathname; + } catch (e) { + // ignore + } + + if (path.endsWith(".wacz")) { + await this.loadPagesForWACZ(url); + } else if (path.endsWith(".json")) { + if (!url.startsWith("http://") && !url.startsWith("https://")) { + const blob = await openAsBlob(url); + url = URL.createObjectURL(blob); + } + + const resp = await fetch(url); + const json = await resp.json(); + + // if json contains pages, just load them directly + if (json.pages) { + await this.loadPagesDirect(json.pages); + } else { + // otherwise, parse pages from WACZ files + for (const entry of json.resources) { + if (entry.path) { + await this.loadPages(entry.path); + } + } + } + } else { + logger.warn("Unknown replay source", { url }, "replay"); + } + } + + async loadPagesForWACZ(url: string) { + const loader = new WACZLoader(url); + await loader.init(); + + let count = 0; + + const pagesReader = await loader.loadFile("pages/pages.jsonl"); + + if (pagesReader) { + for await (const buff of pagesReader.iterLines()) { + await this.addPage(buff, count++); + if (this.limitHit) { + break; + } + } + } + + const extraPagesReader = await loader.loadFile("pages/extraPages.jsonl"); + + if (extraPagesReader) { + for await (const buff of extraPagesReader.iterLines()) { + await this.addPage(buff, count++); + if (this.limitHit) { + break; + } + } + } + } + + async loadPagesDirect(pages: ReplayPage[]) { + let depth = 0; + for (const entry of pages) { + const { url, ts, id } = entry; + if (!url) { + continue; + } + if (this.limitHit) { + break; + } + await this.queueUrl(0, url, depth++, 0, {}, ts, id); + } + } + + async addPage(page: string, depth: number) { + let pageData: ReplayPage; + + if (!page.length) { + return; + } + + try { + pageData = JSON.parse(page); + } catch (e) { + console.log(page, e); + return; + } + + const { url, ts, id } = pageData; + if (!url) { + return; + } + + await this.queueUrl(0, url, depth, 0, {}, ts, id); + } + + extraChromeArgs(): string[] { + return [...super.extraChromeArgs(), "--disable-web-security"]; + } + + handleRequestWillBeSent( + params: Protocol.Network.RequestWillBeSentEvent, + page: Page, + ) { + // only handling redirect here, committing last response in redirect chain + const { redirectResponse, type } = params; + if (redirectResponse) { + const { url, status, mimeType } = redirectResponse; + this.addPageResource(url, page, { status, mime: mimeType, type }); + } + } + + async handlePageResourceResponse( + params: Protocol.Network.ResponseReceivedEvent, + page: Page, + ) { + const { response } = params; + const { url, status } = response; + if (!url.startsWith(REPLAY_PREFIX)) { + if (url.startsWith(REPLAY_SOURCE)) { + const { mimeType, fromServiceWorker } = response; + if ( + !fromServiceWorker && + mimeType === "application/json" && + page.frames().length > 1 + ) { + const frame = page.frames()[1]; + const timeoutid = setTimeout(() => { + logger.warn("Reloading RWP Frame, not inited", { url }, "replay"); + frame.evaluate("window.location.reload();"); + }, 10000); + this.reloadTimeouts.set(page, timeoutid); + } else if (fromServiceWorker && mimeType !== "application/json") { + const timeoutid = this.reloadTimeouts.get(page); + if (timeoutid) { + clearTimeout(timeoutid); + this.reloadTimeouts.delete(page); + } + } + } + return; + } + + const { type } = params; + const { mimeType } = response; + + this.addPageResource(url, page, { status, mime: mimeType, type }); + } + + addPageResource( + url: string, + page: Page, + { status, mime, type }: PageInfoValue, + ) { + const inx = url.indexOf("_/"); + if (inx <= 0) { + return; + } + + let replayUrl = url.slice(inx + 2, MAX_URL_LENGTH); + + const pageInfo = this.pageInfos.get(page); + + if (!pageInfo) { + return; + } + + if (replayUrl.startsWith("//")) { + try { + replayUrl = new URL(replayUrl, pageInfo.url).href; + } catch (e) { + // + } + } + + if (replayUrl.startsWith("http://") || replayUrl.startsWith("https://")) { + pageInfo.urls[replayUrl] = { status, mime, type }; + } + } + + async crawlPage(opts: WorkerState): Promise { + await this.writeStats(); + + const { page, data } = opts; + const { url, ts, pageid } = data; + + if (!ts) { + return; + } + + const date = new Date(ts); + + const timestamp = date.toISOString().slice(0, 19).replace(/[T:-]/g, ""); + + logger.info("Loading Replay", { url, timestamp }, "replay"); + + const pageInfo = { + pageid, + urls: {}, + url, + ts: date, + comparison: { resourceCounts: {} }, + counts: { jsErrors: 0 }, + }; + this.pageInfos.set(page, pageInfo); + + await page.evaluate( + (url, ts) => { + const rwp = document.querySelector("replay-web-page"); + if (!rwp) { + return; + } + const p = new Promise((resolve) => { + window.addEventListener( + "message", + (e) => { + if (e.data && e.data.url && e.data.view) { + resolve(); + } + }, + { once: true }, + ); + }); + + rwp.setAttribute("url", url); + rwp.setAttribute("ts", ts ? ts : ""); + return p; + }, + url, + timestamp, + ); + + // optionally reload (todo: reevaluate if this is needed) + // await page.reload(); + + await sleep(10); + + data.isHTMLPage = true; + + // skipping RWP frames + data.filteredFrames = page.frames().slice(SKIP_FRAMES); + + try { + data.title = await data.filteredFrames[0].title(); + } catch (e) { + // ignore + } + + data.favicon = await this.getFavicon(page, {}); + + await this.doPostLoadActions(opts, true); + + await this.compareScreenshots(page, data, url, date); + + await this.compareText(page, data, url, date); + + await this.compareResources(page, data, url, date); + + await this.processPageInfo(page, data); + } + + async compareScreenshots( + page: Page, + state: PageState, + url: string, + date?: Date, + ) { + const origScreenshot = await this.fetchOrigBinary( + page, + "view", + url, + date ? date.toISOString().replace(/[^\d]/g, "") : "", + ); + const { pageid, screenshotView } = state; + + if (!origScreenshot || !origScreenshot.length) { + logger.warn("Orig screenshot missing for comparison", { url }, "replay"); + return; + } + + if (!screenshotView || !screenshotView.length) { + logger.warn( + "Replay screenshot missing for comparison", + { url }, + "replay", + ); + return; + } + + const crawl = PNG.sync.read(origScreenshot); + const replay = PNG.sync.read(screenshotView); + + const { width, height } = replay; + const diff = new PNG({ width, height }); + + const res = pixelmatch(crawl.data, replay.data, diff.data, width, height, { + threshold: 0.1, + alpha: 0, + }); + + const total = width * height; + + const matchPercent = (total - res) / total; + + logger.info( + "Screenshot Diff", + { + url, + diff: res, + matchPercent, + }, + "replay", + ); + + if (res && this.params.qaDebugImageDiff) { + const dir = path.join(this.collDir, "screenshots", pageid || "unknown"); + await fsp.mkdir(dir, { recursive: true }); + await fsp.writeFile(path.join(dir, "crawl.png"), PNG.sync.write(crawl)); + await fsp.writeFile(path.join(dir, "replay.png"), PNG.sync.write(replay)); + await fsp.writeFile(path.join(dir, "diff.png"), PNG.sync.write(diff)); + } + + const pageInfo = this.pageInfos.get(page); + if (pageInfo) { + pageInfo.comparison.screenshotMatch = matchPercent; + } + } + + async compareText(page: Page, state: PageState, url: string, date?: Date) { + const origText = await this.fetchOrigText( + page, + "text", + url, + date ? date.toISOString().replace(/[^\d]/g, "") : "", + ); + const replayText = state.text; + + if (!origText || !replayText) { + logger.warn( + "Text missing for comparison", + { + url, + origTextLen: origText?.length, + replayTextLen: replayText?.length, + }, + "replay", + ); + return; + } + + const dist = levenshtein(origText, replayText); + const maxLen = Math.max(origText.length, replayText.length); + const matchPercent = (maxLen - dist) / maxLen; + logger.info("Levenshtein Dist", { url, dist, matchPercent, maxLen }); + + const pageInfo = this.pageInfos.get(page); + if (pageInfo) { + pageInfo.comparison.textMatch = matchPercent; + } + } + + async compareResources( + page: Page, + state: PageState, + url: string, + date?: Date, + ) { + const origResources = await this.fetchOrigText( + page, + "pageinfo", + url, + date ? date.toISOString().replace(/[^\d]/g, "") : "", + ); + + let origResData: PageInfoRecord | null; + + try { + origResData = JSON.parse(origResources || ""); + } catch (e) { + origResData = null; + } + + const pageInfo: ReplayPageInfoRecord | undefined = this.pageInfos.get(page); + + if (!origResData) { + logger.warn("Original resources missing / invalid", { url }, "replay"); + return; + } + + if (!pageInfo) { + logger.warn("Replay resources missing / invalid", { url }, "replay"); + return; + } + + if (origResData.ts) { + pageInfo.ts = origResData.ts; + } + + const { resourceCounts } = pageInfo.comparison; + + const { good: crawlGood, bad: crawlBad } = this.countResources(origResData); + const { good: replayGood, bad: replayBad } = this.countResources(pageInfo); + + resourceCounts.crawlGood = crawlGood; + resourceCounts.crawlBad = crawlBad; + resourceCounts.replayGood = replayGood; + resourceCounts.replayBad = replayBad; + + logger.info("Resource counts", { url, ...resourceCounts }, "replay"); + } + + countResources(info: PageInfoRecord) { + let good = 0; + let bad = 0; + + for (const [url, { status }] of Object.entries(info.urls)) { + if (!url.startsWith("http")) { + continue; + } + if (url.indexOf("__wb_method") !== -1) { + continue; + } + if (status >= 400) { + bad++; + } else { + good++; + } + } + + return { bad, good }; + } + + async fetchOrigBinary(page: Page, type: string, url: string, ts: string) { + const frame = page.frames()[1]; + if (!frame) { + logger.warn("Replay frame missing", { url }, "replay"); + return; + } + + const replayUrl = REPLAY_PREFIX + `${ts}mp_/urn:${type}:${url}`; + + const binaryString = await frame.evaluate(async (url) => { + const response = await fetch(url, { + method: "GET", + credentials: "include", + }); + if (response.status !== 200) { + return ""; + } + const blob = await response.blob(); + const result = new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsBinaryString(blob); + }); + return result; + }, replayUrl); + + if (!binaryString) { + logger.warn("Couldn't fetch original data", { type, url, ts }, "replay"); + } + + return Buffer.from(binaryString as string, "binary"); + } + + async fetchOrigText(page: Page, type: string, url: string, ts: string) { + const frame = page.frames()[1]; + if (!frame) { + logger.warn("Replay frame missing", { url }, "replay"); + return; + } + + const replayUrl = REPLAY_PREFIX + `${ts}mp_/urn:${type}:${url}`; + + const text = await frame.evaluate(async (url) => { + const response = await fetch(url, { + method: "GET", + credentials: "include", + }); + if (response.status !== 200) { + return ""; + } + return await response.text(); + }, replayUrl); + + if (!text) { + logger.warn("Couldn't fetch original data", { type, url, ts }, "replay"); + } + + return text; + } + + async teardownPage(opts: WorkerOpts) { + const { page } = opts; + await this.processPageInfo(page); + await super.teardownPage(opts); + } + + async processPageInfo(page: Page, state?: PageState) { + const pageInfo = this.pageInfos.get(page); + if (pageInfo) { + if (!pageInfo.urls[pageInfo.url]) { + logger.warn( + "Replay resource: missing top-level page", + { url: pageInfo.url }, + "replay", + ); + } + + if (state) { + const { comparison } = pageInfo; + + // add comparison to page state + (state as ComparisonPageState).comparison = comparison; + } + + const writer = new WARCResourceWriter({ + url: pageInfo.url, + directory: this.archivesDir, + warcPrefix: this.warcPrefix, + date: new Date(), + warcName: "info.warc.gz", + }); + await writer.writeBufferToWARC( + new TextEncoder().encode(JSON.stringify(pageInfo, null, 2)), + "pageinfo", + "application/json", + ); + this.pageInfos.delete(page); + } + } + + protected pageEntryForRedis( + entry: Record, + state: PageState, + ) { + entry.comparison = (state as ComparisonPageState).comparison; + return entry; + } + + createRecorder(): Recorder | null { + return null; + } +} + +class WACZLoader { + url: string; + zipreader: ZipRangeReader; + + constructor(url: string) { + this.url = url; + this.zipreader = null; + } + + async init() { + if (!this.url.startsWith("http://") && !this.url.startsWith("https://")) { + const blob = await openAsBlob(this.url); + this.url = URL.createObjectURL(blob); + } + + const loader = await createLoader({ url: this.url }); + + this.zipreader = new ZipRangeReader(loader); + } + + async loadFile(fileInZip: string) { + const { reader } = await this.zipreader.loadFile(fileInZip); + + if (!reader) { + return null; + } + + if (!reader.iterLines) { + return new AsyncIterReader(reader); + } + + return reader; + } +} diff --git a/src/util/argParser.ts b/src/util/argParser.ts index e64a77666..96912c82f 100644 --- a/src/util/argParser.ts +++ b/src/util/argParser.ts @@ -536,10 +536,21 @@ class ArgParser { choices: SERVICE_WORKER_OPTS, default: "disabled", }, + + qaSource: { + describe: "Required for QA mode. Source (WACZ or multi WACZ) for QA", + type: "string", + }, + + qaDebugImageDiff: { + describe: + "if specified, will write crawl.png, replay.png and diff.png for each page where they're different", + type: "boolean", + }, }; } - parseArgs(argvParams?: string[]) { + parseArgs(argvParams?: string[], isQA = false) { let argv = argvParams || process.argv; if (process.env.CRAWL_ARGS) { @@ -563,7 +574,7 @@ class ArgParser { return origConfig; }, ) - .check((argv) => this.validateArgs(argv)).argv; + .check((argv) => this.validateArgs(argv, isQA)).argv; return { parsed, origConfig }; } @@ -576,7 +587,7 @@ class ArgParser { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - validateArgs(argv: Record) { + validateArgs(argv: Record, isQA: boolean) { argv.crawlId = argv.crawlId || process.env.CRAWL_ID || os.hostname; argv.collection = interpolateFilename(argv.collection, argv.crawlId); @@ -631,33 +642,39 @@ class ArgParser { //logger.debug(`Set netIdleWait to ${argv.netIdleWait} seconds`); } - const scopeOpts = { - scopeType: argv.scopeType, - sitemap: argv.sitemap, - include: argv.include, - exclude: argv.exclude, - depth: argv.depth, - extraHops: argv.extraHops, - }; - argv.scopedSeeds = []; - for (let seed of argv.seeds) { - if (typeof seed === "string") { - seed = { url: seed }; - } + if (!isQA) { + const scopeOpts = { + scopeType: argv.scopeType, + sitemap: argv.sitemap, + include: argv.include, + exclude: argv.exclude, + depth: argv.depth, + extraHops: argv.extraHops, + }; + + for (let seed of argv.seeds) { + if (typeof seed === "string") { + seed = { url: seed }; + } - try { - argv.scopedSeeds.push(new ScopedSeed({ ...scopeOpts, ...seed })); - } catch (e) { - if (argv.failOnFailedSeed) { - logger.fatal(`Invalid Seed "${seed.url}" specified, aborting crawl.`); + try { + argv.scopedSeeds.push(new ScopedSeed({ ...scopeOpts, ...seed })); + } catch (e) { + if (argv.failOnFailedSeed) { + logger.fatal( + `Invalid Seed "${seed.url}" specified, aborting crawl.`, + ); + } } } - } - if (!argv.scopedSeeds.length) { - logger.fatal("No valid seeds specified, aborting crawl."); + if (!argv.scopedSeeds.length) { + logger.fatal("No valid seeds specified, aborting crawl."); + } + } else if (!argv.qaSource) { + logger.fatal("--qaSource required for QA mode!"); } // Resolve statsFilename @@ -673,6 +690,6 @@ class ArgParser { } } -export function parseArgs(argv?: string[]) { - return new ArgParser().parseArgs(argv); +export function parseArgs(argv?: string[], isQA = false) { + return new ArgParser().parseArgs(argv, isQA); } diff --git a/src/util/browser.ts b/src/util/browser.ts index 046bbc59e..6564afd55 100644 --- a/src/util/browser.ts +++ b/src/util/browser.ts @@ -19,22 +19,27 @@ import puppeteer, { Viewport, } from "puppeteer-core"; import { CDPSession, Target, Browser as PptrBrowser } from "puppeteer-core"; +import { Recorder } from "./recorder.js"; + +type BtrixChromeOpts = { + proxy?: boolean; + userAgent?: string | null; + extraArgs?: string[]; +}; type LaunchOpts = { profileUrl: string; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - chromeOptions: Record; + chromeOptions: BtrixChromeOpts; signals: boolean; headless: boolean; // TODO: Fix this the next time the file is edited. // eslint-disable-next-line @typescript-eslint/no-explicit-any emulateDevice?: Record; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ondisconnect?: ((err: any) => NonNullable) | null; + ondisconnect?: ((err: unknown) => NonNullable) | null; swOpt?: ServiceWorkerOpt; + + recording: boolean; }; // ================================================================== @@ -48,9 +53,7 @@ export class Browser { browser?: PptrBrowser | null = null; firstCDP: CDPSession | null = null; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - recorders: any[] = []; + recorders: Recorder[] = []; swOpt?: ServiceWorkerOpt = "disabled"; @@ -66,6 +69,7 @@ export class Browser { emulateDevice = {}, swOpt = "disabled", ondisconnect = null, + recording = true, }: LaunchOpts) { if (this.isLaunched()) { return; @@ -105,7 +109,7 @@ export class Browser { userDataDir: this.profileDir, }; - await this._init(launchOpts, ondisconnect); + await this._init(launchOpts, ondisconnect, recording); } async setupPage({ page }: { page: Page; cdp: CDPSession }) { @@ -116,13 +120,13 @@ export class Browser { switch (this.swOpt) { case "disabled": - logger.info("Service Workers: always disabled", {}, "browser"); + logger.debug("Service Workers: always disabled", {}, "browser"); await page.setBypassServiceWorker(true); break; case "disabled-if-profile": if (this.customProfile) { - logger.info( + logger.debug( "Service Workers: disabled since using profile", {}, "browser", @@ -132,7 +136,7 @@ export class Browser { break; case "enabled": - logger.info("Service Workers: always enabled", {}, "browser"); + logger.debug("Service Workers: always enabled", {}, "browser"); break; } } @@ -195,7 +199,11 @@ export class Browser { }); } - chromeArgs({ proxy = true, userAgent = null, extraArgs = [] } = {}) { + chromeArgs({ + proxy = true, + userAgent = null, + extraArgs = [], + }: BtrixChromeOpts) { // Chrome Flags, including proxy server const args = [ // eslint-disable-next-line no-use-before-define @@ -347,6 +355,7 @@ export class Browser { launchOpts: PuppeteerLaunchOptions, // eslint-disable-next-line @typescript-eslint/ban-types ondisconnect: Function | null = null, + recording: boolean, ) { this.browser = await puppeteer.launch(launchOpts); @@ -354,7 +363,9 @@ export class Browser { this.firstCDP = await target.createCDPSession(); - await this.serviceWorkerFetch(); + if (recording) { + await this.serviceWorkerFetch(); + } if (ondisconnect) { this.browser.on("disconnected", (err) => ondisconnect(err)); @@ -497,8 +508,6 @@ export class Browser { }); } - // TODO: Fix this the next time the file is edited. - async evaluateWithCLI( _: unknown, frame: Frame, diff --git a/src/util/logger.ts b/src/util/logger.ts index dccb75abf..1ae95d37f 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -48,6 +48,7 @@ export const LOG_CONTEXT_TYPES = [ "crawlStatus", "links", "sitemap", + "replay", ] as const; export type LogContext = (typeof LOG_CONTEXT_TYPES)[number]; diff --git a/src/util/replayserver.ts b/src/util/replayserver.ts new file mode 100644 index 000000000..cc40b42ab --- /dev/null +++ b/src/util/replayserver.ts @@ -0,0 +1,146 @@ +import fs from "fs"; +import fsp from "fs/promises"; +import http, { IncomingMessage, ServerResponse } from "http"; +import path from "path"; + +const replayHTML = fs.readFileSync( + new URL("../../html/replay.html", import.meta.url), + { encoding: "utf8" }, +); + +const swJS = fs.readFileSync(new URL("../../html/rwp/sw.js", import.meta.url), { + encoding: "utf8", +}); + +const uiJS = fs.readFileSync(new URL("../../html/rwp/ui.js", import.meta.url), { + encoding: "utf8", +}); + +// ============================================================================ +const PORT = 9990; + +// ============================================================================ +export class ReplayServer { + sourceUrl: string; + origFileSource: string | null; + sourceContentType: string | null; + sourceSize?: number; + + constructor(sourceUrlOrFile: string) { + if ( + sourceUrlOrFile.startsWith("http://") || + sourceUrlOrFile.startsWith("https://") + ) { + this.sourceUrl = sourceUrlOrFile; + this.origFileSource = null; + this.sourceContentType = null; + } else { + this.origFileSource = sourceUrlOrFile; + const ext = path.extname(sourceUrlOrFile); + this.sourceUrl = `/source${ext}`; + + switch (ext) { + case ".wacz": + this.sourceContentType = "application/wacz+zip"; + break; + + case ".json": + this.sourceContentType = "application/json"; + break; + + default: + this.sourceContentType = "application/octet-stream"; + } + } + const httpServer = http.createServer((req, res) => + this.handleRequest(req, res), + ); + httpServer.listen(PORT); + } + + get homePage() { + return `http://localhost:${PORT}/`; + } + + async handleRequest(request: IncomingMessage, response: ServerResponse) { + const parsedUrl = new URL( + request.url || "", + `http://${request.headers.host}`, + ); + const pathname = parsedUrl.pathname; + + switch (pathname) { + case "/": + response.writeHead(200, { "Content-Type": "text/html" }); + response.end(replayHTML.replace("$SOURCE", this.sourceUrl)); + return; + + case "/sw.js": + case "/sw.js?serveIndex=1": + case "/replay/sw.js": + case "/replay/sw.js?serveIndex=1": + response.writeHead(200, { "Content-Type": "application/javascript" }); + response.end(swJS); + return; + + case "/ui.js": + response.writeHead(200, { "Content-Type": "application/javascript" }); + response.end(uiJS); + return; + + case this.sourceUrl: + if (this.sourceContentType && this.origFileSource) { + if (!this.sourceSize) { + const { size } = await fsp.stat(this.origFileSource); + this.sourceSize = size; + } + const { opts, status, contentRange, contentLength } = + this.getRespOptsForRequest(request, this.sourceSize); + response.writeHead(status, { + "Accept-Ranges": "bytes", + "Content-Type": this.sourceContentType, + "Content-Length": contentLength, + "Content-Range": contentRange, + }); + //console.log(request.method, contentRange, opts); + if (request.method === "GET") { + fs.createReadStream(this.origFileSource, opts).pipe(response); + } else { + response.end(); + } + break; + } + // falls through + + default: + response.writeHead(404, { "Content-Type": "application/json" }); + response.end(JSON.stringify({ error: "not_found" })); + return; + } + } + + getRespOptsForRequest(request: IncomingMessage, total: number) { + const range = request.headers["range"] || ""; + const array = range.match(/bytes=(\d+)-(\d*)/); + let contentRange = undefined; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const opts: Record = {}; + if (array) { + opts.start = parseInt(array[1]); + opts.end = parseInt(array[2]); + if (isNaN(opts.end)) { + opts.end = undefined; + } + const end = opts.end || total - 1; + contentRange = `bytes ${opts.start}-${end}/${total}`; + return { + status: 206, + opts, + contentRange, + contentLength: end - opts.start + 1, + }; + } + return { status: 200, opts, contentRange, contentLength: total }; + } +} diff --git a/src/util/screenshots.ts b/src/util/screenshots.ts index f1c7211ca..c64d876de 100644 --- a/src/util/screenshots.ts +++ b/src/util/screenshots.ts @@ -3,48 +3,63 @@ import sharp from "sharp"; import { WARCResourceWriter } from "./warcresourcewriter.js"; import { logger, formatErr } from "./logger.js"; import { Browser } from "./browser.js"; +import { Page } from "puppeteer-core"; +import { PageState } from "./state.js"; // ============================================================================ -type ScreenShotType = { - type: string; +type ScreenShotDesc = { + type: "png" | "jpeg"; omitBackground: boolean; fullPage: boolean; + encoding: "binary"; }; -export const screenshotTypes: Record = { +type ScreeshotType = "view" | "thumbnail" | "fullPage"; + +export const screenshotTypes: Record = { view: { type: "png", omitBackground: true, fullPage: false, + encoding: "binary", }, thumbnail: { type: "jpeg", omitBackground: true, fullPage: false, + encoding: "binary", }, fullPage: { type: "png", omitBackground: true, fullPage: true, + encoding: "binary", }, }; +export type ScreenshotOpts = { + browser: Browser; + page: Page; + url: string; + directory: string; + warcPrefix: string; +}; + export class Screenshots extends WARCResourceWriter { browser: Browser; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - page: any; + page: Page; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(opts: any) { + constructor(opts: ScreenshotOpts) { super({ ...opts, warcName: "screenshots.warc.gz" }); this.browser = opts.browser; this.page = opts.page; } - async take(screenshotType = "view") { + async take( + screenshotType: ScreeshotType = "view", + state: PageState | null = null, + ) { try { if (screenshotType !== "fullPage") { await this.browser.setViewport(this.page, { @@ -54,6 +69,9 @@ export class Screenshots extends WARCResourceWriter { } const options = screenshotTypes[screenshotType]; const screenshotBuffer = await this.page.screenshot(options); + if (state && screenshotType === "view") { + state.screenshotView = screenshotBuffer; + } await this.writeBufferToWARC( screenshotBuffer, screenshotType, diff --git a/src/util/state.ts b/src/util/state.ts index ad365680e..92f5e40f0 100644 --- a/src/util/state.ts +++ b/src/util/state.ts @@ -33,6 +33,8 @@ export type QueueEntry = { seedId: number; depth: number; extraHops: number; + ts?: number; + pageid?: string; }; // ============================================================================ @@ -66,6 +68,7 @@ export class PageState { isHTMLPage?: boolean; text?: string; + screenshotView?: Buffer; favicon?: string; skipBehaviors = false; @@ -79,7 +82,10 @@ export class PageState { this.seedId = redisData.seedId; this.depth = redisData.depth; this.extraHops = redisData.extraHops || 0; - this.pageid = uuidv4(); + if (redisData.ts) { + this.ts = new Date(redisData.ts); + } + this.pageid = redisData.pageid || uuidv4(); this.status = 0; } } @@ -472,12 +478,26 @@ return 0; //async addToQueue({url : string, seedId, depth = 0, extraHops = 0} = {}, limit = 0) { async addToQueue( - { url, seedId, depth = 0, extraHops = 0 }: QueueEntry, + { + url, + seedId, + depth = 0, + extraHops = 0, + ts = 0, + pageid = undefined, + }: QueueEntry, limit = 0, ) { const added = this._timestamp(); const data: QueueEntry = { added, url, seedId, depth, extraHops }; + if (ts) { + data.ts = ts; + } + if (pageid) { + data.pageid = pageid; + } + // return codes // 0 - url queued successfully // 1 - url queue size limit reached diff --git a/src/util/textextract.ts b/src/util/textextract.ts index 2d681e489..876b35a24 100644 --- a/src/util/textextract.ts +++ b/src/util/textextract.ts @@ -2,17 +2,25 @@ import { WARCResourceWriter } from "./warcresourcewriter.js"; import { logger } from "./logger.js"; import { CDPSession, Protocol } from "puppeteer-core"; +// ============================================================================ +type TextExtractOpts = { + url: string; + directory: string; + warcPrefix: string; + skipDocs: number; +}; + // ============================================================================ export abstract class BaseTextExtract extends WARCResourceWriter { cdp: CDPSession; lastText: string | null = null; text: string | null = null; + skipDocs: number = 0; - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(cdp: CDPSession, opts: any) { + constructor(cdp: CDPSession, opts: TextExtractOpts) { super({ ...opts, warcName: "text.warc.gz" }); this.cdp = cdp; + this.skipDocs = opts.skipDocs || 0; } async extractAndStoreText( @@ -83,7 +91,7 @@ export class TextExtractViaSnapshot extends BaseTextExtract { const accum: string[] = []; - for (const doc of documents) { + for (const doc of documents.slice(this.skipDocs)) { const nodeValues = doc.nodes.nodeValue || []; const nodeNames = doc.nodes.nodeName || []; const nodeTypes = doc.nodes.nodeType || []; diff --git a/src/util/warcresourcewriter.ts b/src/util/warcresourcewriter.ts index 6fe88b8ba..769cb3d36 100644 --- a/src/util/warcresourcewriter.ts +++ b/src/util/warcresourcewriter.ts @@ -2,10 +2,17 @@ import fs from "fs"; import path from "path"; import * as warcio from "warcio"; +// =========================================================================== +export type WARCResourceWriterOpts = { + url: string; + directory: string; + date?: Date; + warcName: string; + warcPrefix: string; +}; + +// =========================================================================== export class WARCResourceWriter { - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - page: any; url: string; directory: string; warcName: string; @@ -17,13 +24,7 @@ export class WARCResourceWriter { date, warcPrefix, warcName, - }: { - url: string; - directory: string; - date: Date; - warcPrefix: string; - warcName: string; - }) { + }: WARCResourceWriterOpts) { this.url = url; this.directory = directory; this.warcName = path.join(this.directory, warcPrefix + warcName); diff --git a/src/util/worker.ts b/src/util/worker.ts index 119bfbe9d..fdc160ac8 100644 --- a/src/util/worker.ts +++ b/src/util/worker.ts @@ -14,7 +14,6 @@ const NEW_WINDOW_TIMEOUT = 20; const TEARDOWN_TIMEOUT = 10; const FINISHED_TIMEOUT = 60; -// =========================================================================== export type WorkerOpts = { page: Page; cdp: CDPSession; @@ -39,6 +38,7 @@ export class PageWorker { maxPageTime: number; reuseCount = 0; + alwaysReuse: boolean; page?: Page | null; cdp?: CDPSession | null; @@ -55,27 +55,22 @@ export class PageWorker { markCrashed?: (reason: string) => void; crashBreak?: Promise; - recorder: Recorder; + recorder: Recorder | null; constructor( id: WorkerId, crawler: Crawler, maxPageTime: number, - collDir: string, + alwaysReuse = false, ) { this.id = id; this.crawler = crawler; this.maxPageTime = maxPageTime; + this.alwaysReuse = alwaysReuse; this.logDetails = { workerid: this.id }; - this.recorder = new Recorder({ - workerid: id, - collDir, - crawler: this.crawler, - }); - - this.crawler.browser.recorders.push(this.recorder); + this.recorder = this.crawler.createRecorder(this.id); } async closePage() { @@ -133,19 +128,18 @@ export class PageWorker { } async initPage(url: string): Promise { - if ( - !this.crashed && - this.page && - this.opts && - ++this.reuseCount <= MAX_REUSE && - this.isSameOrigin(url) - ) { + let reuse = !this.crashed && !!this.opts && !!this.page; + if (!this.alwaysReuse) { + ++this.reuseCount; + reuse = this.reuseCount <= MAX_REUSE && this.isSameOrigin(url); + } + if (reuse) { logger.debug( "Reusing page", { reuseCount: this.reuseCount, ...this.logDetails }, "worker", ); - return this.opts; + return this.opts!; } else if (this.page) { await this.closePage(); } @@ -176,7 +170,7 @@ export class PageWorker { this.cdp = cdp; this.callbacks = {}; const directFetchCapture = this.recorder - ? (x: string) => this.recorder.directFetchCapture(x) + ? (x: string) => this.recorder!.directFetchCapture(x) : null; this.opts = { page, @@ -405,10 +399,11 @@ export async function runWorkers( crawler: Crawler, numWorkers: number, maxPageTime: number, - collDir: string, + alwaysReuse = false, ) { logger.info(`Creating ${numWorkers} workers`, {}, "worker"); + const workers = []; let offset = 0; // automatically set worker start by ordinal in k8s @@ -426,7 +421,7 @@ export async function runWorkers( } for (let i = 0; i < numWorkers; i++) { - workers.push(new PageWorker(i + offset, crawler, maxPageTime, collDir)); + workers.push(new PageWorker(i + offset, crawler, maxPageTime, alwaysReuse)); } await Promise.allSettled(workers.map((worker) => worker.run())); diff --git a/tests/add-exclusion.test.js b/tests/add-exclusion.test.js index 87f10f4ef..71a1d240f 100644 --- a/tests/add-exclusion.test.js +++ b/tests/add-exclusion.test.js @@ -1,6 +1,10 @@ import { exec } from "child_process"; import Redis from "ioredis"; +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + test("dynamically add exclusion while crawl is running", async () => { let callback = null; @@ -12,7 +16,7 @@ test("dynamically add exclusion while crawl is running", async () => { try { exec( - "docker run -p 36379:6379 -e CRAWL_ID=test -v $PWD/test-crawls:/crawls -v $PWD/tests/fixtures:/tests/fixtures webrecorder/browsertrix-crawler crawl --collection add-exclusion --url https://webrecorder.net/ --scopeType prefix --limit 20 --logging debug --debugAccessRedis", + "docker run -p 36382:6379 -e CRAWL_ID=test -v $PWD/test-crawls:/crawls -v $PWD/tests/fixtures:/tests/fixtures webrecorder/browsertrix-crawler crawl --collection add-exclusion --url https://webrecorder.net/ --scopeType prefix --limit 20 --logging debug --debugAccessRedis", { shell: "/bin/bash" }, callback, ); @@ -20,18 +24,18 @@ test("dynamically add exclusion while crawl is running", async () => { console.log(error); } - await new Promise((resolve) => setTimeout(resolve, 3000)); + await sleep(3000); - const redis = new Redis("redis://127.0.0.1:36379/0", { lazyConnect: true }); + const redis = new Redis("redis://127.0.0.1:36382/0", { lazyConnect: true, retryStrategy: () => null }) - await redis.connect({ maxRetriesPerRequest: 50 }); + await redis.connect(); while (true) { if (Number(await redis.zcard("test:q")) > 1) { break; } - await new Promise((resolve) => setTimeout(resolve, 500)); + await sleep(500); } const uids = await redis.hkeys("test:status"); @@ -48,6 +52,5 @@ test("dynamically add exclusion while crawl is running", async () => { expect(stdout.indexOf("Add Exclusion") > 0).toBe(true); expect(stdout.indexOf("Removing excluded URL") > 0).toBe(true); - - await redis.disconnect(); }); + diff --git a/tests/qa_compare.test.js b/tests/qa_compare.test.js new file mode 100644 index 000000000..fd0d4ed6c --- /dev/null +++ b/tests/qa_compare.test.js @@ -0,0 +1,75 @@ +import child_process from "child_process"; +import fs from "fs"; +import { Redis } from "ioredis"; + +const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); + +test("run initial crawl with text and screenshots to prepare for QA", async () => { + fs.rmSync("./test-crawls/qa-wr-net", { recursive: true, force: true }); + + child_process.execSync( + "docker run -v $PWD/test-crawls:/crawls webrecorder/browsertrix-crawler crawl --url https://webrecorder.net/ --url https://webrecorder.net/about --url https://browsertrix.com/ --scopeType page --collection qa-wr-net --text to-warc --screenshot view --generateWACZ", + ); + + expect( + fs.existsSync("test-crawls/collections/qa-wr-net/qa-wr-net.wacz"), + ).toBe(true); +}); + +test("run QA comparison, with write pages to redis", async () => { + fs.rmSync("./test-crawls/qa-wr-net-replay", { recursive: true, force: true }); + + const child = child_process.exec( + "docker run -p 36380:6379 -v $PWD/test-crawls:/crawls webrecorder/browsertrix-crawler qa --qaSource /crawls/collections/qa-wr-net/qa-wr-net.wacz --collection qa-wr-net-replay --crawlId test --qaDebugImageDiff --writePagesToRedis --debugAccessRedis", + ); + + // detect crawler exit + let crawler_exited = false; + child.on("exit", function () { + crawler_exited = true; + }); + + const redis = new Redis("redis://127.0.0.1:36380/0", { lazyConnect: true, retryStrategy: () => null }); + + await sleep(3000); + + await redis.connect({ maxRetriesPerRequest: 50 }); + + let count = 0; + + while (count < 3) { + const res = await redis.lpop("test:pages"); + if (!res) { + if (crawler_exited) { + break; + } + await sleep(100); + continue; + } + const json = JSON.parse(res); + expect(json).toHaveProperty("id"); + expect(json).toHaveProperty("url"); + expect(json).toHaveProperty("ts"); + expect(json).toHaveProperty("title"); + expect(json).toHaveProperty("loadState"); + expect(json).toHaveProperty("comparison"); + + expect(json.comparison).toHaveProperty("screenshotMatch"); + expect(json.comparison).toHaveProperty("textMatch"); + expect(json.comparison).toHaveProperty("resourceCounts"); + + expect(json.comparison.resourceCounts).toHaveProperty("crawlGood"); + expect(json.comparison.resourceCounts).toHaveProperty("crawlBad"); + expect(json.comparison.resourceCounts).toHaveProperty("replayGood"); + expect(json.comparison.resourceCounts).toHaveProperty("replayBad"); + + count++; + } + + expect(count).toBe(3); + + // wait for crawler exit + while (!crawler_exited) { + await sleep(100); + } +}); diff --git a/tests/saved-state.test.js b/tests/saved-state.test.js index 00b222749..03c96bc1c 100644 --- a/tests/saved-state.test.js +++ b/tests/saved-state.test.js @@ -117,9 +117,11 @@ test("check parsing saved state + page done + queue present", () => { test("check crawl restarted with saved state", async () => { let containerId = null; + const port = 36379; + try { containerId = execSync( - `docker run -d -p 36379:6379 -e CRAWL_ID=test -v $PWD/test-crawls:/crawls -v $PWD/tests/fixtures:/tests/fixtures webrecorder/browsertrix-crawler crawl --collection int-state-test --url https://webrecorder.net/ --config /crawls/collections/int-state-test/crawls/${savedStateFile} --debugAccessRedis --limit 5`, + `docker run -d -p ${port}:6379 -e CRAWL_ID=test -v $PWD/test-crawls:/crawls -v $PWD/tests/fixtures:/tests/fixtures webrecorder/browsertrix-crawler crawl --collection int-state-test --url https://webrecorder.net/ --config /crawls/collections/int-state-test/crawls/${savedStateFile} --debugAccessRedis --limit 5`, { encoding: "utf-8" }, ); } catch (error) { @@ -128,14 +130,11 @@ test("check crawl restarted with saved state", async () => { await sleep(2000); - const redis = new Redis("redis://127.0.0.1:36379/0", { lazyConnect: true }); + const redis = new Redis(`redis://127.0.0.1:${port}/0`, { lazyConnect: true, retryStrategy: () => null }); try { await redis.connect({ maxRetriesPerRequest: 100, - retryStrategy(times) { - return times < 100 ? 1000 : null; - }, }); await sleep(2000); @@ -150,11 +149,5 @@ test("check crawl restarted with saved state", async () => { console.log(e); } finally { await waitContainer(containerId); - - try { - await redis.disconnect(); - } catch (e) { - // ignore - } } }); diff --git a/tests/sitemap-parse.test.js b/tests/sitemap-parse.test.js index b4f3ab6d6..7815da0f7 100644 --- a/tests/sitemap-parse.test.js +++ b/tests/sitemap-parse.test.js @@ -1,7 +1,6 @@ import child_process from "child_process"; import Redis from "ioredis"; - function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -31,20 +30,17 @@ async function waitContainer(containerId) { } async function runCrawl(numExpected, url, sitemap="", limit=0) { - const containerId = child_process.execSync(`docker run -d -p 36379:6379 -e CRAWL_ID=test webrecorder/browsertrix-crawler crawl --url ${url} --sitemap ${sitemap} --limit ${limit} --context sitemap --logging debug --debugAccessRedis`, {encoding: "utf-8"}); + const containerId = child_process.execSync(`docker run -d -p 36381:6379 -e CRAWL_ID=test webrecorder/browsertrix-crawler crawl --url ${url} --sitemap ${sitemap} --limit ${limit} --context sitemap --logging debug --debugAccessRedis`, {encoding: "utf-8"}); - await sleep(2000); + await sleep(3000); - const redis = new Redis("redis://127.0.0.1:36379/0", { lazyConnect: true }); + const redis = new Redis("redis://127.0.0.1:36381/0", { lazyConnect: true, retryStrategy: () => null }); let finished = 0; try { await redis.connect({ maxRetriesPerRequest: 100, - retryStrategy(times) { - return times < 100 ? 1000 : null; - }, }); while (true) { @@ -58,11 +54,6 @@ async function runCrawl(numExpected, url, sitemap="", limit=0) { console.error(e); } finally { await waitContainer(containerId); - try { - await redis.disconnect(); - } catch (e) { - // ignore - } } expect(finished).toBeGreaterThanOrEqual(numExpected); @@ -79,4 +70,3 @@ test("test sitemap with limit", async () => { test("test sitemap with limit, specific URL", async () => { await runCrawl(1900, "https://www.mozilla.org/", "https://www.mozilla.org/sitemap.xml", 2000); }); - diff --git a/yarn.lock b/yarn.lock index b366e920f..407227839 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,14 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" @@ -29,11 +37,24 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + "@babel/compat-data@^7.19.3": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.4.tgz#95c86de137bf0317f3a570e1b6e996b427299747" integrity sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw== +"@babel/compat-data@^7.23.5": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.1.tgz#31c1f66435f2a9c329bb5716a6d6186c516c3742" + integrity sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA== + "@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.6.tgz#7122ae4f5c5a37c0946c066149abd8e75f81540f" @@ -55,6 +76,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.23.9": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3" + integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.1" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.24.1" + "@babel/parser" "^7.24.1" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.19.6", "@babel/generator@^7.7.2": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.6.tgz#9e481a3fe9ca6261c972645ae3904ec0f9b34a1d" @@ -64,6 +106,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0" + integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A== + dependencies: + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-compilation-targets@^7.19.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca" @@ -74,11 +126,27 @@ browserslist "^4.21.3" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-function-name@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" @@ -87,6 +155,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -94,6 +170,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -101,6 +184,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.22.15": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + "@babel/helper-module-transforms@^7.19.6": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz#6c52cc3ac63b70952d33ee987cbee1c9368b533f" @@ -115,6 +205,17 @@ "@babel/traverse" "^7.19.6" "@babel/types" "^7.19.4" +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" @@ -132,6 +233,13 @@ dependencies: "@babel/types" "^7.19.4" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -139,11 +247,23 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.23.4": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" @@ -154,11 +274,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + "@babel/helpers@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.4.tgz#42154945f87b8148df7203a25c31ba9a73be46c5" @@ -168,6 +298,15 @@ "@babel/traverse" "^7.19.4" "@babel/types" "^7.19.4" +"@babel/helpers@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" + integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + "@babel/highlight@^7.12.13": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" @@ -186,6 +325,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.12.13": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" @@ -196,6 +345,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.6.tgz#b923430cb94f58a7eae8facbffa9efd19130e7f8" integrity sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA== +"@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" + integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -303,6 +457,15 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" +"@babel/template@^7.22.15", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + "@babel/template@^7.3.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" @@ -312,7 +475,7 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.19.4", "@babel/traverse@^7.19.6", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.19.4", "@babel/traverse@^7.19.6": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.6.tgz#7b4c865611df6d99cb131eec2e8ac71656a490dc" integrity sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ== @@ -328,6 +491,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== + dependencies: + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.1" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" @@ -345,6 +524,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -417,115 +605,115 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.2.1.tgz#5f2c62dcdd5ce66e94b6d6729e021758bceea090" - integrity sha512-MF8Adcw+WPLZGBiNxn76DOuczG3BhODTcMlDCA4+cFi41OkaY/lyI0XUUhi73F88Y+7IHoGmD80pN5CtxQUdSw== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.2.1.tgz#30af794ebd73bfb87cd8ba36718738dfe38b772e" - integrity sha512-kuLKYqnqgerXkBUwlHVxeSuhSnd+JMnMCLfU98bpacBSfWEJPegytDh3P2m15/JHzet32hGGld4KR4OzMb6/Tg== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^29.2.1" - "@jest/reporters" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.2.0" - jest-config "^29.2.1" - jest-haste-map "^29.2.1" - jest-message-util "^29.2.1" - jest-regex-util "^29.2.0" - jest-resolve "^29.2.1" - jest-resolve-dependencies "^29.2.1" - jest-runner "^29.2.1" - jest-runtime "^29.2.1" - jest-snapshot "^29.2.1" - jest-util "^29.2.1" - jest-validate "^29.2.1" - jest-watcher "^29.2.1" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - pretty-format "^29.2.1" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.2.1.tgz#acb1994fbd5ad02819a1a34a923c531e6923b665" - integrity sha512-EutqA7T/X6zFjw6mAWRHND+ZkTPklmIEWCNbmwX6uCmOrFrWaLbDZjA+gePHJx6fFMMRvNfjXcvzXEtz54KPlg== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.2.1" + jest-mock "^29.7.0" -"@jest/expect-utils@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.2.1.tgz#eae61c90f2066540f60d23b8f254f03b7869b22f" - integrity sha512-yr4aHNg5Z1CjKby5ozm7sKjgBlCOorlAoFcvrOQ/4rbZRfgZQdnmh7cth192PYIgiPZo2bBXvqdOApnAMWFJZg== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - jest-get-type "^29.2.0" + jest-get-type "^29.6.3" -"@jest/expect@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.2.1.tgz#8d99be3886ebfcffd6cabb2b46602a301b976ffe" - integrity sha512-o14R2t2tHHHudwji43UKkzmmH49xfF5T++FQBK2tl88qwuBWQOcx7fNUYl+mA/9TPNAN0FkQ3usnpyS8FUwsvQ== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - expect "^29.2.1" - jest-snapshot "^29.2.1" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.2.1.tgz#786d60e8cb60ca70c9f913cb49fcc77610c072bb" - integrity sha512-KWil+8fef7Uj/P/PTZlPKk1Pw117wAmr71VWFV8ZDtRtkwmTG8oY4IRf0Ss44J2y5CYRy8d/zLOhxyoGRENjvA== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/types" "^29.2.1" - "@sinonjs/fake-timers" "^9.1.2" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.2.1" - jest-mock "^29.2.1" - jest-util "^29.2.1" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.2.1.tgz#6933beb8b4e43b990409a19c462fde7b71210e63" - integrity sha512-Z4EejYPP1OPVq2abk1+9urAwJqkgw5jB2UJGlPjb5ZwzPQF8WLMcigKEfFzZb2OHhEVPP0RZD0/DbVTY1R6iQA== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^29.2.1" - "@jest/expect" "^29.2.1" - "@jest/types" "^29.2.1" - jest-mock "^29.2.1" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.2.1.tgz#599e4376823751fdda50f2ca97243e013da10c4d" - integrity sha512-sCsfUKM/yIF4nNed3e/rIgVIS58EiASGMDEPWqItfLZ9UO1ALW2ASDNJzdWkxEt0T8o2Ztj619G0KKrvK+McAw== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.1" - "@jest/types" "^29.2.1" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -533,81 +721,81 @@ glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - jest-worker "^29.2.1" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^29.2.0": - version "29.2.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" - integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: - "@jridgewell/trace-mapping" "^0.3.15" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.2.1.tgz#f42dbf7b9ae465d0a93eee6131473b8bb3bd2edb" - integrity sha512-lS4+H+VkhbX6z64tZP7PAUwPqhwj3kbuEHcaLuaBuB+riyaX7oa1txe0tXgrFj5hRWvZKvqO7LZDlNWeJ7VTPA== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.2.1.tgz#cafd2c5f3528c70bd4cc243800459ac366e480cc" - integrity sha512-O/pnk0/xGj3lxPVNwB6HREJ7AYvUdyP2xo/s14/9Dtf091HoOeyIhWLKQE/4HzB8lNQBMo6J5mg0bHz/uCWK7w== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^29.2.1" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" + jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.2.1.tgz#f3d8154edd19cdbcaf1d6646bd8f4ff7812318a2" - integrity sha512-xup+iEuaIRSQabQaeqxaQyN0vg1Dctrp9oTObQsNf3sZEowTIa5cANYuoyi8Tqhg4GCqEVLTf18KW7ii0UeFVA== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.2.1" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" + convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" - jest-regex-util "^29.2.0" - jest-util "^29.2.1" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" -"@jest/types@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.2.1.tgz#ec9c683094d4eb754e41e2119d8bdaef01cf6da0" - integrity sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -631,22 +819,46 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -654,6 +866,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -817,24 +1037,24 @@ unbzip2-stream "1.4.3" yargs "17.7.1" -"@sinclair/typebox@^0.24.1": - version "0.24.50" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.50.tgz#35ee4db4ab8f3a8ff56490c51f92445d2776451e" - integrity sha512-k8ETQOOQDg5FtK7y9KJWpsGLik+QlPmIi8zzl/dGUgshV2QitprkFlCR/AemjWOTyKn9UwSSGRTzLVotvgCjYQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" @@ -900,6 +1120,11 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/js-levenshtein@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.3.tgz#a6fd0bdc8255b274e5438e0bfb25f154492d1106" + integrity sha512-jd+Q+sD20Qfu9e2aEXogiO3vpOC1PYJOUdyN9gvs4Qrvkg4wF43L5OhqrPeokdv8TL0/mXoYfpkcoGZMNN2pkQ== + "@types/js-yaml@^4.0.8": version "4.0.8" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.8.tgz#7574e422d70d4a1b41f517d1d9abc61be2299a97" @@ -922,10 +1147,19 @@ dependencies: undici-types "~5.25.1" -"@types/prettier@^2.1.5": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" - integrity sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow== +"@types/pixelmatch@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.6.tgz#fba6de304ac958495f27d85989f5c6bb7499a686" + integrity sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg== + dependencies: + "@types/node" "*" + +"@types/pngjs@^6.0.4": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.4.tgz#9a457aebabd944efde1a773a0fa1d74933e8021b" + integrity sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ== + dependencies: + "@types/node" "*" "@types/sax@^1.2.7": version "1.2.7" @@ -1256,15 +1490,15 @@ b4a@^1.6.4: resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== -babel-jest@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.2.1.tgz#213c47e28072de11bdb98c9d29b89f2ab99664f1" - integrity sha512-gQJwArok0mqoREiCYhXKWOgUhElJj9DpnssW6GL8dG7ARYqHEhrM9fmPHTjdqEGRVXZAd6+imo3/Vwa8TjLcsw== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^29.2.1" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.2.0" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -1280,10 +1514,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094" - integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -1308,12 +1542,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc" - integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^29.2.0" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -1391,6 +1625,16 @@ browserslist@^4.21.3: node-releases "^2.0.6" update-browserslist-db "^1.0.9" +browserslist@^4.22.2: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + browsertrix-behaviors@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/browsertrix-behaviors/-/browsertrix-behaviors-0.5.3.tgz#f987075790b0fd970814f57195e8525277ddd2a0" @@ -1457,7 +1701,12 @@ caniuse-lite@^1.0.30001400: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001423.tgz#57176d460aa8cd85ee1a72016b961eb9aca55d91" integrity sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ== -chalk@^2.0.0: +caniuse-lite@^1.0.30001587: + version "1.0.30001600" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz#93a3ee17a35aa6a9f0c6ef1b2ab49507d1ab9079" + integrity sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ== + +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1575,18 +1824,36 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + crc@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/crc/-/crc-4.3.2.tgz#49b7821cbf2cf61dfd079ed93863bbebd5469b9a" integrity sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A== +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + cross-fetch@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" @@ -1620,7 +1887,7 @@ data-uri-to-buffer@^5.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== -debug@4, debug@4.3.4, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1646,10 +1913,10 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== deep-extend@^0.6.0: version "0.6.0" @@ -1702,10 +1969,10 @@ devtools-protocol@0.0.1147663: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== -diff-sequences@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6" - integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== dir-glob@^3.0.1: version "3.0.1" @@ -1733,10 +2000,15 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +electron-to-chromium@^1.4.668: + version "1.4.715" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz#bb16bcf2a3537962fccfa746b5c98c5f7404ff46" + integrity sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -2003,16 +2275,16 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expect@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.2.1.tgz#25752d0df92d3daa5188dc8804de1f30759658cf" - integrity sha512-BJtA754Fba0YWRWHgjKUMTA3ltWarKgITXHQnbZ2mTxTXC4yMQlR0FI7HkB3fJYkhWBf4qjNiqvg3LDtXCcVRQ== +expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "^29.2.1" - jest-get-type "^29.2.0" - jest-matcher-utils "^29.2.1" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" extract-zip@2.0.1: version "2.0.1" @@ -2693,7 +2965,7 @@ istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -2704,6 +2976,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" + integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -2730,366 +3013,363 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289" - integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" + jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.2.1.tgz#1385353d9bca6acf58f916068bbeffcfc95bef02" - integrity sha512-W+ZQQ5ln4Db2UZNM4NJIeasnhCdDhSuYW4eLgNAUi0XiSSpF634Kc5wiPvGiHvTgXMFVn1ZgWIijqhi9+kLNLg== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/environment" "^29.2.1" - "@jest/expect" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^29.2.1" - jest-matcher-utils "^29.2.1" - jest-message-util "^29.2.1" - jest-runtime "^29.2.1" - jest-snapshot "^29.2.1" - jest-util "^29.2.1" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" p-limit "^3.1.0" - pretty-format "^29.2.1" + pretty-format "^29.7.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.2.1.tgz#fbfa90b87b27a04e1041cc9d33ee80f32e2f2528" - integrity sha512-UIMD5aNqvPKpdlJSaeUAoLfxsh9TZvOkaMETx5qXnkboc317bcbb0eLHbIj8sFBHdcJAIAM+IRKnIU7Wi61MBw== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.2.1" - jest-util "^29.2.1" - jest-validate "^29.2.1" - prompts "^2.0.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.2.1.tgz#2182af014d6c73978208626335db5134803dd183" - integrity sha512-EV5F1tQYW/quZV2br2o88hnYEeRzG53Dfi6rSG3TZBuzGQ6luhQBux/RLlU5QrJjCdq3LXxRRM8F1LP6DN1ycA== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.2.1" - "@jest/types" "^29.2.1" - babel-jest "^29.2.1" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.2.1" - jest-environment-node "^29.2.1" - jest-get-type "^29.2.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.2.1" - jest-runner "^29.2.1" - jest-util "^29.2.1" - jest-validate "^29.2.1" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.2.1" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.2.1.tgz#027e42f5a18b693fb2e88f81b0ccab533c08faee" - integrity sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^29.2.0" - jest-get-type "^29.2.0" - pretty-format "^29.2.1" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82" - integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.2.1.tgz#6b0a88ee85c2ba27b571a6010c2e0c674f5c9b29" - integrity sha512-sGP86H/CpWHMyK3qGIGFCgP6mt+o5tu9qG4+tobl0LNdgny0aitLXs9/EBacLy3Bwqy+v4uXClqJgASJWcruYw== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^29.2.0" - jest-util "^29.2.1" - pretty-format "^29.2.1" - -jest-environment-node@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.2.1.tgz#f90311d0f0e8ef720349f83c97a076e403f90665" - integrity sha512-PulFKwEMz6nTAdLUwglFKei3b/LixwlRiqTN6nvPE1JtrLtlnpd6LXnFI1NFHYJGlTmIWilMP2n9jEtPPKX50g== - dependencies: - "@jest/environment" "^29.2.1" - "@jest/fake-timers" "^29.2.1" - "@jest/types" "^29.2.1" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.2.1" - jest-util "^29.2.1" + jest-mock "^29.7.0" + jest-util "^29.7.0" -jest-get-type@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" - integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.2.1.tgz#f803fec57f8075e6c55fb5cd551f99a72471c699" - integrity sha512-wF460rAFmYc6ARcCFNw4MbGYQjYkvjovb9GBT+W10Um8q5nHq98jD6fHZMDMO3tA56S8XnmNkM8GcA8diSZfnA== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.2.0" - jest-util "^29.2.1" - jest-worker "^29.2.1" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.2.1.tgz#ec551686b7d512ec875616c2c3534298b1ffe2fc" - integrity sha512-1YvSqYoiurxKOJtySc+CGVmw/e1v4yNY27BjWTVzp0aTduQeA7pdieLiW05wTYG/twlKOp2xS/pWuikQEmklug== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - jest-get-type "^29.2.0" - pretty-format "^29.2.1" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-matcher-utils@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.2.1.tgz#2bf876c5f891b33786aadf5d65d5da5970744122" - integrity sha512-hUTBh7H/Mnb6GTpihbLh8uF5rjAMdekfW/oZNXUMAXi7bbmym2HiRpzgqf/zzkjgejMrVAkPdVSQj+32enlUww== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^29.2.1" - jest-get-type "^29.2.0" - pretty-format "^29.2.1" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.2.1.tgz#3a51357fbbe0cc34236f17a90d772746cf8d9193" - integrity sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.2.1" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.2.1.tgz#a0d361cffcb28184fa9c5443adbf591fa5759775" - integrity sha512-NDphaY/GqyQpTfnTZiTqqpMaw4Z0I7XnB7yBgrT6IwYrLGxpOhrejYr4ANY4YvO2sEGdd8Tx/6D0+WLQy7/qDA== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-util "^29.2.1" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" - integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.2.1.tgz#8d717dd41dc615fef1d412d395ea3deccfb1b9fa" - integrity sha512-o3mUGX2j08usj1jIAIE8KmUVpqVAn54k80kI27ldbZf2oJn6eghhB6DvJxjrcH40va9CQgWTfU5f2Ag/MoUqgQ== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - jest-regex-util "^29.2.0" - jest-snapshot "^29.2.1" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.2.1.tgz#a4d2f76db88aeb6ec5f5453c9a40b52483d17799" - integrity sha512-1dJTW76Z9622Viq4yRcwBuEXuzGtE9B2kdl05RC8Om/lAzac9uEgC+M8Q5osVidbuBPmxm8wSrcItYhca2ZAtQ== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.1" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.2.1.tgz#885afe64661cb2f51f84c1b97afb713d1093c124" - integrity sha512-PojFI+uVhQ4u4YZKCN/a3yU0/l/pJJXhq1sW3JpCp8CyvGBYGddRFPKZ1WihApusxqWRTHjBJmGyPWv6Av2lWA== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^29.2.1" - "@jest/environment" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - emittery "^0.10.2" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^29.2.0" - jest-environment-node "^29.2.1" - jest-haste-map "^29.2.1" - jest-leak-detector "^29.2.1" - jest-message-util "^29.2.1" - jest-resolve "^29.2.1" - jest-runtime "^29.2.1" - jest-util "^29.2.1" - jest-watcher "^29.2.1" - jest-worker "^29.2.1" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.2.1.tgz#62e3a23c33710ae4d9c3304dda851a5fb225b574" - integrity sha512-PSQ880OoIW9y8E6/jjhGn3eQNgNc6ndMzCZaKqy357bv7FqCfSyYepu3yDC6Sp1Vkt+GhP2M/PVgldS2uZSFZg== - dependencies: - "@jest/environment" "^29.2.1" - "@jest/fake-timers" "^29.2.1" - "@jest/globals" "^29.2.1" - "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.1" - "@jest/types" "^29.2.1" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" - jest-message-util "^29.2.1" - jest-mock "^29.2.1" - jest-regex-util "^29.2.0" - jest-resolve "^29.2.1" - jest-snapshot "^29.2.1" - jest-util "^29.2.1" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.2.1.tgz#f3843b3099c8fec7e6218dea18cc506f10ea5d30" - integrity sha512-KZdLD7iEz5M4ZYd+ezZ/kk73z+DtNbk/yJ4Qx7408Vb0CCuclJIZPa/HmIwSsCfIlOBNcYTKufr7x/Yv47oYlg== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.2.1" - "@jest/transform" "^29.2.1" - "@jest/types" "^29.2.1" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.2.1" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^29.2.1" - jest-get-type "^29.2.0" - jest-haste-map "^29.2.1" - jest-matcher-utils "^29.2.1" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^29.2.1" - semver "^7.3.5" + pretty-format "^29.7.0" + semver "^7.5.3" -jest-util@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.2.1.tgz#f26872ba0dc8cbefaba32c34f98935f6cf5fc747" - integrity sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.2.1.tgz#db814ce12c4c7e4746044922762e56eb177d066c" - integrity sha512-DZVX5msG6J6DL5vUUw+++6LEkXUsPwB5R7fsfM7BXdz2Ipr0Ib046ak+8egrwAR++pvSM/5laxLK977ieIGxkQ== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^29.2.0" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^29.2.1" + pretty-format "^29.7.0" -jest-watcher@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.2.1.tgz#1cb91f8aa9e77b1332af139944ad65e51430d7c3" - integrity sha512-7jFaHUaRq50l4w/f6RuY713bvI5XskMmjWCE54NGYcY74fLkShS8LucXJke1QfGnwDSCoIqGnGGGKPwdaBYz2Q== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^29.2.1" + emittery "^0.13.1" + jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.2.1.tgz#8ba68255438252e1674f990f0180c54dfa26a3b1" - integrity sha512-ROHTZ+oj7sBrgtv46zZ84uWky71AoYi0vEV9CdEtc1FQunsoAGe5HbQmW76nI5QWdvECVPrSi1MCVUmizSavMg== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.2.1" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.2.1.tgz#352ec0b81a0e436691d546d984cd7d8f72ffd26a" - integrity sha512-K0N+7rx+fv3Us3KhuwRSJt55MMpZPs9Q3WSO/spRZSnsalX8yEYOTQ1PiSN7OvqzoRX4JEUXCbOJRlP4n8m5LA== +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^29.2.1" + jest-cli "^29.7.0" js-levenshtein@^1.1.6: version "1.1.6" @@ -3146,6 +3426,11 @@ json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -3225,6 +3510,13 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3393,6 +3685,11 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" @@ -3673,6 +3970,13 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== +pixelmatch@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.3.0.tgz#5e5321a7abedfb7962d60dbf345deda87cb9560a" + integrity sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q== + dependencies: + pngjs "^6.0.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -3680,6 +3984,16 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pngjs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" + integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== + +pngjs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26" + integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow== + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -3708,12 +4022,12 @@ prettier@3.0.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== -pretty-format@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.2.1.tgz#86e7748fe8bbc96a6a4e04fa99172630907a9611" - integrity sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA== +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -3788,6 +4102,11 @@ puppeteer-core@^20.8.2: devtools-protocol "0.0.1147663" ws "8.13.0" +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + pvtsutils@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" @@ -3905,10 +4224,10 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.20.0: version "1.22.1" @@ -3976,6 +4295,11 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^7.3.5: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -3983,6 +4307,13 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" +semver@^7.5.3: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -4493,6 +4824,14 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + update-browserslist-db@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -4641,7 +4980,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^4.0.1: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -4682,6 +5021,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"