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"