diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b59b242..8a67392e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,47 @@ +# [v7.0.0](https://github.com/coder-hxl/x-crawl/compare/v6.0.1...v7.0.0) (2023-04-26) + +### 🚨 Breaking Changes + +- Fingerprint upgrade: + - The fingerprint of the advanced writing method is renamed to fingerprints, which is an array writing method, which stores objects of the DetailTargetFingerprintCommon type, which is convenient for customization. Internally, the objects inside will be randomly assigned to the target. + - Adjustment of crawlPage fingerprint options: the maximum width and height of the fingerprint configuration of advanced writing and detailed target writing are changed to optional. +- Proxy upgrade: create a crawler instance, change the proxy of the advanced writing method and the detailed target writing method to the object writing method, with three attributes: urls, switchByHttpStatus and switchByErrorCount, urls can set multiple proxy URLs, and the internal default uses the first one first, switchByHttpStatus Set which non-compliant response status codes need to switch the proxy, and switchByErrorCount sets how many times the proxy needs to be switched when errors such as timeouts arrive. The proxy rotation feature needs to be used with error retries. +- Return value type adjustment: CrawlCommonRes, CrawlPageSingleRes, CrawlDataSingleRes and CrawlFileSingleRes are renamed to CrawlCommonResult, CrawlPageSingleResult, CrawlDataSingleResult and CrawlFileSingleResult respectively + +### 🚀 Features + +- It is possible to cancel the configuration of the upper-level unified setting by setting null in the option. +- The userAgent option in DetailTargetFingerprintCommon overrides the object notation and allows customization of the maximum and minimum values of the major version, minor version, and revision number inside. Each crawl target gets a new userAgent . +- A new proxyDetails property is added to the crawling results to record the proxy status. +- Added 'random' attribute value to mobile option of fingerprint configuration, allowing internal randomization. +- Terminal prompts are simplified and color adjusted. + +### 🐞 Bug fixes + +- Unable to create multiple levels of non-existent folders on linux systems. + +--- + +### 🚨 重大改变 + +- 指纹升级: + - 进阶写法的 fingerprint 改名为 fingerprints ,为数组写法,里面存放 DetailTargetFingerprintCommon 类型的对象,方便定制。内部会将里面的对象随机分配给目标。 + - crawlPage 的指纹选项调整:进阶写法和详细目标写法的指纹配置的最大宽高改为可选项。 +- 代理升级:创建爬虫实例、进阶写法以及详细目标写法的 proxy 更改为对象写法, 拥有 urls、switchByHttpStatus 以及 switchByErrorCount 这三个属性,urls 可以设置多个代理 URL ,内部默认先采用第一个,switchByHttpStatus 设置遇到哪些不符合的响应状态码需要切换代理,switchByErrorCount 设置像超时等错误时到达多少次需要切换代理。该代理轮换功能需要配合错误重试才能使用。 +- 返回值类型调整:CrawlCommonRes、CrawlPageSingleRes、CrawlDataSingleRes 以及 CrawlFileSingleRes 分别更名为 CrawlCommonResult、CrawlPageSingleResult、CrawlDataSingleResult 以及 CrawlFileSingleResult + +### 🚀 特征 + +- 可以通过在选项设置为 null 取消上级统一设置的配置。 +- DetailTargetFingerprintCommon 里的 userAgent 选项改写对象写法,并允许定制里面的主版本、次版本以及修订号的最大值和最小值。每个爬取目标都会获取一个新的 userAgent 。 +- 爬取结果新增 proxyDetails 属性,记录代理状态。 +- 指纹配置的 mobile 选项添加 'random' 属性值,允许由内部随机决定。 +- 终端提示信息进行简化以及颜色调整。 + +### 🐞 漏洞修复 + +- 在 linux 系统上无法创建多级不存在的文件夹。 + # [v6.0.1](https://github.com/coder-hxl/x-crawl/compare/v6.0.0...v6.0.1) (2023-04-21) ### 🚀 Features @@ -6,7 +50,7 @@ --- -### 特征 +### 🚀 特征 - 完善文档。 diff --git a/README.md b/README.md index 61f5a091..1afd81ce 100644 --- a/README.md +++ b/README.md @@ -9,20 +9,21 @@ x-crawl is a flexible Node.js multipurpose crawler library. The usage is flexibl ## Features - **🔥 Asynchronous Synchronous** - Just change the mode property to toggle asynchronous or synchronous crawling mode. -- **⚙️Multiple purposes** - It can crawl pages, crawl interfaces, crawl files and poll crawls to meet the needs of various scenarios. +- **⚙️ Multiple purposes** - It can crawl pages, crawl interfaces, crawl files and poll crawls to meet the needs of various scenarios. - **🖋️ Flexible writing style** - The same crawling API can be adapted to multiple configurations, and each configuration method is very unique. -- **👀Device Fingerprinting** - Zero configuration or custom configuration, avoid fingerprinting to identify and track us from different locations. - **⏱️ Interval Crawling** - No interval, fixed interval and random interval to generate or avoid high concurrent crawling. -- **🔄 Failed Retry** - Avoid crawling failure due to transient problems, unlimited retries. +- **🔄 Failed Retry** - Avoid crawling failure due to short-term problems, and customize the number of retries. +- **➡️ Proxy Rotation** - Auto-rotate proxies with failure retry, custom error times and HTTP status codes. +- **👀 Device Fingerprinting** - Zero configuration or custom configuration, avoid fingerprinting to identify and track us from different locations. - **🚀 Priority Queue** - According to the priority of a single crawling target, it can be crawled ahead of other targets. - **☁️ Crawl SPA** - Crawl SPA (Single Page Application) to generate pre-rendered content (aka "SSR" (Server Side Rendering)). - **⚒️ Control Page** - You can submit form, keyboard input, event operation, generate screenshots of the page, etc. -- **🧾 Capture Record** - Capture and record the crawled information, and highlight it on the console. +- **🧾 Capture Record** - Capture and record crawling, and use colored strings to remind in the terminal. - **🦾 TypeScript** - Own types, implement complete types through generics. ## Relationship with Puppeteer -The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built in, you only need to pass in some configuration options to complete some operations, and the result will expose Brower instances and Page instances. +The crawlPage API has built-in [puppeteer](https://github.com/puppeteer/puppeteer), you only need to pass in some configuration options to complete some operations, the result will expose the Brower instance and Page instance, you get Brower instance and Page instance will be intact, x-crawl will not rewrite them. # Table of Contents @@ -48,9 +49,10 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [onBeforeSaveItemFile](#onBeforeSaveItemFile) - [Start Polling](#Start-Polling) - [Config Priority](#Config-Priority) - - [Custom Device Fingerprint](#Custom-Device-Fingerprint) - [Interval Time](#Interval-Time) - [Fail Retry](#Fail-Retry) + - [Rotate Proxy](#Rotate-Proxy) + - [Custom Device Fingerprint](#Custom-Device-Fingerprint) - [Priority Queue](#Priority-Queue) - [About Results](#About-Results) - [TypeScript](#TypeScript) @@ -85,21 +87,20 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [Type](#Type-4) - [Example](#Example-5) - [Types](#Types) - - [API Config](#API-config) + - [API Config](#API-Config) - [XCrawlConfig](#XCrawlConfig) - - [Detail target config](#Detail-target-config) + - [Detail Target Config](#Detail-Target-Config) - [CrawlPageDetailTargetConfig](#CrawlPageDetailTargetConfig) - [CrawlDataDetailTargetConfig](#CrawlDataDetailTargetConfig) - [CrawlFileDetailTargetConfig](#CrawlFileDetailTargetConfig) - - [Advanced config](#Advanced-config) + - [Advanced Config](#Advanced-Config) - [CrawlPageAdvancedConfig](#CrawlPageAdvancedConfig) - [CrawlDataAdvancedConfig](#CrawlDataAdvancedConfig) - [CrawlFileAdvancedConfig](#CrawlFileAdvancedConfig) - [StartPollingConfig](#StartPollingConfig) - - [Crawl other config](#Crawl-other-config) + - [Crawl Other Config](#Crawl-Other-Config) - [CrawlCommonConfig](#CrawlCommonConfig) - [DetailTargetFingerprintCommon](#DetailTargetFingerprintCommon) - - [AdvancedFingerprintCommon](#AdvancedFingerprintCommon) - [Mobile](#Mobile) - [Platform](#Platform) - [PageCookies](#PageCookies) @@ -107,13 +108,15 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [IntervalTime](#IntervalTime) - [API Result](#API-Result) - [XCrawlInstance](#XCrawlInstance) - - [CrawlCommonRes](#CrawlCommonRes) - - [CrawlPageSingleRes](#CrawlPageSingleRes) - - [CrawlDataSingleRes](#CrawlDataSingleRes) - - [CrawlFileSingleRes](#CrawlFileSingleRes) + - [CrawlCommonResult](#CrawlCommonResult) + - [CrawlPageSingleResult](#CrawlPageSingleResult) + - [CrawlDataSingleResult](#CrawlDataSingleResult) + - [CrawlFileSingleResult](#CrawlFileSingleResult) - [API Other](#API-Other) - [AnyObject](#AnyObject) - [More](#More) + - [Community](#Community) + - [Issues](#Issues) ## Install @@ -125,14 +128,14 @@ npm install x-crawl ## Example -Take some pictures of Airbnb hawaii experience and Plus listings automatically every day as an example: +Take the automatic acquisition of some photos of experiences and homes around the world every day as an example: ```js // 1.Import module ES/CJS import xCrawl from 'x-crawl' // 2.Create a crawler instance -const myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } }) +const myXCrawl = xCrawl({maxRetry: 3,intervalTime: { max: 3000, min: 2000 }}) // 3.Set the crawling task /* @@ -141,23 +144,31 @@ const myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } }) */ myXCrawl.startPolling({ d: 1 }, async (count, stopPolling) => { // Call crawlPage API to crawl Page - const res = await myXCrawl.crawlPage([ - 'https://zh.airbnb.com/s/hawaii/experiences', - 'https://zh.airbnb.com/s/hawaii/plus_homes' - ]) + const res = await myXCrawl.crawlPage({ + targets: [ + 'https://www.airbnb.cn/s/experiences', + 'https://www.airbnb.cn/s/plus_homes' + ], + viewport: { width: 1920, height: 1080 } + }) // Store the image URL to targets const targets = [] - const elSelectorMap = ['.c14whb16', '.a1stauiv'] + const elSelectorMap = ['._fig15y', '._aov0j6'] for (const item of res) { const { id } = item const { page } = item.data - // Gets the URL of the page's wheel image element - const boxHandle = await page.$(elSelectorMap[id - 1]) - const urls = await boxHandle!.$$eval('picture img', (imgEls) => { - return imgEls.map((item) => item.src) - }) + // Wait for the page to load + await new Promise((r) => setTimeout(r, 300)) + + // Gets the URL of the page image + const urls = await page!.$$eval( + `${elSelectorMap[id - 1]} img`, + (imgEls) => { + return imgEls.map((item) => item.src) + } + ) targets.push(...urls) // Close page @@ -304,7 +315,7 @@ myXCrawl.crawlPage('https://www.example.com').then(async (res) => { Lifecycle functions owned by the crawlPage API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete ##### onCrawlItemComplete @@ -340,7 +351,7 @@ myXCrawl.crawlData({ targets }).then((res) => { Life cycle functions owned by crawlData API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete ##### onCrawlItemComplete @@ -374,7 +385,7 @@ myXCrawl Life cycle functions owned by crawlFile API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete - onBeforeSaveItemFile: Callback before saving the file @@ -448,7 +459,7 @@ Some common configurations can be set in these three places: - Application instance configuration (global) - Advanced configuration (partial) -- detailed target configuration (separately) +- Detailed target configuration (separately) The priority is: detailed target configuration > advanced configuration > application instance configuration @@ -457,73 +468,45 @@ Take crawlPage to crawl two pages as an example: ```js import xCrawl from 'x-crawl' -// Application instance configuration -const myXCrawl = xCrawl({ - intervalTime: { max: 3000, min: 1000 } -}) - -// advanced configuration -myXCrawl.crawlPage({ - targets: [ - 'https://www.example.com/page-1', - { - // Detailed target configuration - url: 'https://www.example.com/page-1', - viewport: { width: 1920, height: 1080 } - } - ], - intervalTime: 1000, - viewport: { width: 800, height: 600 } +// Application instance configurationconst testXCrawl = xCrawl({ + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } }) -``` - -### Custom Device Fingerprint - -Customize the configuration of device fingerprints to avoid identifying and tracking us from different locations through fingerprint recognition. - -Multiple information can be passed in the fingerprint through advanced usage, and internally it will help you randomly assign each target to targets. It is also possible to set a specific fingerprint for a target directly with the detailed target configuration. - -Take crawlPage as an example: - -```js -import xCrawl from 'x-crawl' - -const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) -myXCrawl +// Advanced configuration +testXCrawl .crawlPage({ targets: [ 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + // Detailed target configuration { - // Specify the fingerprint - url: 'https://www.example.com/page-2', - fingerprint: { - maxWidth: 1980, - minWidth: 1200, - maxHeight: 1080, - minHidth: 800, - platform: 'Android' - } + url: 'https://www.example.com/page-3', + proxy: { urls: ['https://www.example.com/proxy-5'] } } ], - fingerprint: { - // set fingerprint for each target in targets - maxWidth: 1980, - maxHeight: 1080, - userAgents: [ - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36', - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0' + maxRetry: 10, + proxy: { + urls: [ + 'https://www.example.com/proxy-3', + 'https://www.example.com/proxy-4' ], - platforms: ['Chromium OS', 'iOS', 'Linux', 'macOS', 'Windows'] + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] } }) .then((res) => {}) +}) ``` -For more fingerprint options, you can go to the corresponding configuration to view. - -In the above example, the interval time is set in both **Application Instance Configuration** and **Advanced Configuration**, then the interval time of **Advanced Configuration** will prevail. If the viewport is set in **Advanced Configuration** and **Detailed Target Configuration**, then the second target will be based on the viewport of its **Detailed Target Configuration**. +In the above example, **Proxy** is set in **Application Instance Configuration**, **Advanced Configuration** and **Detailed Target Configuration**, page3 will use its own proxy configuration, page1 and page2 will use the proxy configuration of the advanced configuration. ### Interval Time @@ -555,6 +538,8 @@ The intervalTime option defaults to undefined . If there is a setting value, it It can avoid crawling failure due to temporary problems, and will wait for the end of this round of crawling targets to crawl again. +You can create crawler application instance, advanced usage, detailed target these three places Settings. + ```js import xCrawl from 'x-crawl' @@ -567,6 +552,169 @@ myXCrawl The maxRetry attribute determines how many times to retry. +### Rotate Proxy + +With failed retries, custom error times and HTTP status codes, the proxy is automatically rotated for crawling targets. + +You can create crawler application instance, advanced usage, detailed target these three places Settings. + +Take crawlPage as an example: + +```js +import xCrawl from 'x-crawl' + +const testXCrawl = xCrawl() + +testXCrawl + .crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + 'https://www.example.com/page-4', + // Undelegate for this target + { url: 'https://www.example.com/page-6', proxy: null }, + // Set the proxy individually for this target + { + url: 'https://www.example.com/page-6', + proxy: { + urls: [ + 'https://www.example.com/proxy-4', + 'https://www.example.com/proxy-5' + ], + switchByErrorCount: 3 + } + } + ], + maxRetry: 10, + // Set the proxy uniformly for this target + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } + }) + .then((res) => {}) +``` + +**Note:** This function needs to cooperate with failure retry to work normally. + +### Custom Device Fingerprint + +Customize the configuration of device fingerprints to avoid identifying and tracking us from different locations through fingerprint recognition. + +Multiple information can be passed in fingerprints through advanced usage, and internally it will help you randomly assign each target to targets. It is also possible to set a specific fingerprint for a target directly with the detailed target configuration. + +Take crawlPage as an example: + +```js +import xCrawl from 'x-crawl' + +const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) + +myXCrawl.crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + // Cancel the fingerprint for this target + { url: 'https://www.example.com/page-4', fingerprint: null }, + // Set a separate fingerprint for this target + { + url: 'https://www.example.com/page-5', + fingerprint: { + mobile: 'random', + platform: 'Windows', + acceptLanguage: `zh-CN,zh;q=0.9,en;q=0.8`, + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { name: 'Chrome', maxMinorVersion: 10, maxPatchVersion: 5615 }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 } + ] + } + } + } + ], + // Set fingerprints uniformly for this target + fingerprints: [ + // Device fingerprint 1 + { + maxWidth: 1024, + maxHeight: 800, + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + // Browser version + maxMajorVersion: 112, + minMajorVersion: 100, + maxMinorVersion: 20, + maxPatchVersion: 5000 + }, + { + name: 'Safari', + maxMajorVersion: 537, + minMajorVersion: 500, + maxMinorVersion: 36, + maxPatchVersion: 5000 + } + ] + } + }, + // Device fingerprint 2 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 91, + minMajorVersion: 88, + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 }, + { name: 'Edg', maxMinorVersion: 10, maxPatchVersion: 864 } + ] + } + }, + // Device fingerprint 3 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', + versions: [ + { + name: 'Firefox', + maxMajorVersion: 47, + minMajorVersion: 43, + maxMinorVersion: 10, + maxPatchVersion: 5000 + } + ] + } + } + ] +}) +``` + +For more fingerprint options, you can go to the corresponding configuration to view. + ### Priority Queue A priority queue allows a crawl target to be sent first. @@ -595,6 +743,7 @@ Each crawl target will generate a detail object, which will contain the followin - isSuccess: Whether to crawl successfully - maxRetry: The maximum number of retries for this crawling target - retryCount: The number of times the crawling target has been retried +- proxyDetails: record the proxy situation - crawlErrorQueue: Error collection of the crawl target - data: the crawling data of the crawling target @@ -657,23 +806,23 @@ The crawlPage API is a function. A type is an [overloaded function](https://www. type crawlPage = { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } ``` @@ -684,7 +833,7 @@ type crawlPage = { **Return value type:** -- Look at the [CrawlPageSingleRes](#CrawlPageSingleRes) type +- Look at the [CrawlPageSingleResult](#CrawlPageSingleResult) type #### Example @@ -808,23 +957,23 @@ The crawlData API is a function. A type is an [overloaded function](https://www. type crawlData = { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } ``` @@ -835,7 +984,7 @@ type crawlData = { **Return value type:** -- Look at the [CrawlDataSingleRes](#CrawlDataSingleRes) type +- Look at the [CrawlDataSingleResult](#CrawlDataSingleResult) type #### Example @@ -965,18 +1114,18 @@ The crawlFile API is a function. A type is an [overloaded function](https://www. type crawlFile = { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } ``` @@ -987,7 +1136,7 @@ type crawlFile = { **Return value type:** -- Look at the [CrawlFileSingleRes](#CrawlFileSingleRes) type +- Look at the [CrawlFileSingleResult](#CrawlFileSingleResult) type #### Example @@ -1128,7 +1277,7 @@ myXCrawl.startPolling({ h: 2, m: 30 }, (count, stopPolling) => { ## Types -### API config +### API Config #### XCrawlConfig @@ -1165,9 +1314,9 @@ export interface CrawlPageDetailTargetConfig extends CrawlCommonConfig { viewport?: Viewport | null // puppeteer fingerprint?: | (DetailTargetFingerprintCommon & { - maxWidth: number + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number }) | null @@ -1231,7 +1380,7 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { - extension: string - fingerprint: undefined -#### Advanced config +#### Advanced Config ##### CrawlPageAdvancedConfig @@ -1239,27 +1388,26 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlPageDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon & { - maxWidth: number + fingerprints?: (DetailTargetFingerprintCommon & { + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number - } + })[] headers?: AnyObject cookies?: PageCookies - viewport?: Viewport // puppeteer + viewport?: Viewport - onCrawlItemComplete?: (crawlPageSingleRes: CrawlPageSingleRes) => void + onCrawlItemComplete?: (crawlPageSingleResult: CrawlPageSingleResult) => void } ``` **Default Value** - targets: undefined - - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - cookies: undefined - viewport: undefined @@ -1271,11 +1419,13 @@ export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlDataDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject - onCrawlItemComplete?: (crawlDataSingleRes: CrawlDataSingleRes) => void + onCrawlItemComplete?: ( + crawlDataSingleResult: CrawlDataSingleResult + ) => void } ``` @@ -1283,7 +1433,7 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - onCrawlItemComplete: undefined @@ -1293,13 +1443,13 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlFileDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject storeDir?: string extension?: string - onCrawlItemComplete?: (crawlFileSingleRes: CrawlFileSingleRes) => void + onCrawlItemComplete?: (crawlFileSingleResult: CrawlFileSingleResult) => void onBeforeSaveItemFile?: (info: { id: number fileName: string @@ -1313,7 +1463,7 @@ export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - storeDir: \_\_dirname - extension: string @@ -1336,15 +1486,19 @@ export interface StartPollingConfig { - h: undefined - m: undefined -#### Crawl other config +#### Crawl Other Config ##### CrawlCommonConfig ```ts export interface CrawlCommonConfig { - timeout?: number - proxy?: string - maxRetry?: number + timeout?: number | null + proxy?: { + urls: string[] + switchByHttpStatus?: number[] + switchByErrorCount?: number + } | null + maxRetry?: number | null } ``` @@ -1358,45 +1512,34 @@ export interface CrawlCommonConfig { ```ts export interface DetailTargetFingerprintCommon { - userAgent?: string ua?: string + mobile?: '?0' | '?1' | 'random' platform?: Platform platformVersion?: string - mobile?: Mobile acceptLanguage?: string + userAgent?: { + value: string + versions?: { + name: string + maxMajorVersion?: number + minMajorVersion?: number + maxMinorVersion?: number + minMinorVersion?: number + maxPatchVersion?: number + minPatchVersion?: number + }[] + } } ``` **Default Value** -- userAgent: undefined - ua: undefined +- mobile: undefined - platform: undefined - platformVersion: undefined -- mobile: undefined - acceptLanguage: undefined - -##### AdvancedFingerprintCommon - -```ts -export interface AdvancedFingerprintCommon { - userAgents?: string[] - uas?: string[] - platforms?: Platform[] - platformVersions?: string[] - mobiles?: Mobile[] - acceptLanguages?: string[] -} -``` - -**Default Value** - -- userAgents: undefined -- uas: undefined -- platforms: undefined -- platformVersions: undefined -- mobiles: undefined -- acceptLanguages: undefined +- userAgent: undefined ##### Mobile @@ -1423,8 +1566,8 @@ export type Platform = ```ts export type PageCookies = | string - | Protocol.Network.CookieParam - | Protocol.Network.CookieParam[] + | Protocol.Network.CookieParam // puppeteer + | Protocol.Network.CookieParam[] // puppeteer ``` ##### Method @@ -1459,7 +1602,7 @@ export type Method = export type IntervalTime = number | { max: number; min?: number } ``` -### API result +### API Result #### XCrawlInstance @@ -1468,62 +1611,62 @@ export interface XCrawlInstance { crawlPage: { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } crawlData: { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } crawlFile: { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } startPolling: ( @@ -1533,28 +1676,30 @@ export interface XCrawlInstance { } ``` -#### CrawlCommonRes +#### CrawlCommonResult ```ts -export interface CrawlCommonRes { +export interface CrawlCommonResult { id: number isSuccess: boolean maxRetry: number retryCount: number + proxyDetails: ProxyDetails crawlErrorQueue: Error[] } ``` -- id: Generated according to the order of crawling targets, if there is a priority, it will be generated according to the priority -- isSuccess: Whether to crawl successfully -- maxRetry: The maximum number of retries for this crawling target -- retryCount: The number of times the crawling target has been retried -- crawlErrorQueue: Error collection of the crawl target +- id:Generated according to the order in which the target is climbed, or by priority, if any +- isSuccess:Whether the climb is successful +- maxRetry:Maximum number of retries of the crawl target +- retryCount:Maximum number of retries of the crawl target +- proxyDetails:Record agent status +- crawlErrorQueue:Error collection for the crawl target -#### CrawlPageSingleRes +#### CrawlPageSingleResult ```ts -export interface CrawlPageSingleRes extends CrawlCommonRes { +export interface CrawlPageSingleResult extends CrawlCommonResult { data: { browser: Browser // puppeteer response: HTTPResponse | null // puppeteer @@ -1563,25 +1708,25 @@ export interface CrawlPageSingleRes extends CrawlCommonRes { } ``` -#### CrawlDataSingleRes +#### CrawlDataSingleResult ```ts -export interface CrawlDataSingleRes extends CrawlCommonRes { +export interface CrawlDataSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined - headers: IncomingHttpHeaders // node http + headers: IncomingHttpHeaders // nodejs http data: D } | null } ``` -#### CrawlFileSingleRes +#### CrawlFileSingleResult ```ts -export interface CrawlFileSingleRes extends CrawlCommonRes { +export interface CrawlFileSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined - headers: IncomingHttpHeaders // node http + headers: IncomingHttpHeaders // nodejs http data: { isSuccess: boolean fileName: string @@ -1606,6 +1751,10 @@ export interface AnyObject extends Object { ## More -If you have **problems, needs, good suggestions** please raise **Issues** in https://github.com/coder-hxl/x-crawl/issues. +### Community + +**GitHub Discussions:** May be discussed through [GitHub Discussions](https://github.com/coder-hxl/x-crawl/discussions). + +### Issues -Thank you all for your support. +If you have questions, needs, or good suggestions, you can raise them at [GitHub Issues](https://github.com/coder-hxl/x-crawl/issues). diff --git a/assets/cn/crawler-result.png b/assets/cn/crawler-result.png index e6e233e0..e5369955 100644 Binary files a/assets/cn/crawler-result.png and b/assets/cn/crawler-result.png differ diff --git a/assets/cn/crawler.png b/assets/cn/crawler.png index 0c44f114..dd2a554b 100644 Binary files a/assets/cn/crawler.png and b/assets/cn/crawler.png differ diff --git a/assets/en/crawler-result.png b/assets/en/crawler-result.png index 55f3c2b4..e5369955 100644 Binary files a/assets/en/crawler-result.png and b/assets/en/crawler-result.png differ diff --git a/assets/en/crawler.png b/assets/en/crawler.png index 0bd3560e..dd2a554b 100644 Binary files a/assets/en/crawler.png and b/assets/en/crawler.png differ diff --git a/docs/cn.md b/docs/cn.md index 2867c06e..59c8a760 100644 --- a/docs/cn.md +++ b/docs/cn.md @@ -2,7 +2,7 @@ [English](https://github.com/coder-hxl/x-crawl#x-crawl) | 简体中文 -x-crawl 是一个灵活的 Node.js 多功能爬虫库。用法灵活,并且内置众多功能用于爬页面、爬接口、爬文件等。 +x-crawl 是一个灵活的 Node.js 多功能爬虫库。用法灵活,并内置众多功能用于爬页面、爬接口、爬文件等。 > 如果你也喜欢 x-crawl ,可以给 [x-crawl 存储库](https://github.com/coder-hxl/x-crawl) 点个 star 支持一下,感谢大家的支持! @@ -11,18 +11,19 @@ x-crawl 是一个灵活的 Node.js 多功能爬虫库。用法灵活,并且内 - **🔥 异步同步** - 只需更改一下 mode 属性即可切换异步或同步爬取模式。 - **⚙️ 多种用途** - 可爬页面、爬接口、爬文件以及轮询爬,满足各种场景需求。 - **🖋️ 写法灵活** - 同种爬取 API 适配多种配置,每种配置方式都非常独特。 -- **👀 设备指纹** - 零配置或自定义配置,避免指纹识别从不同位置识别并跟踪我们。 - **⏱️ 间隔爬取** - 无间隔、固定间隔以及随机间隔,产生或避免高并发爬取。 -- **🔄 失败重试** - 避免因短暂的问题而造成爬取失败,无限制重试次数。 +- **🔄 失败重试** - 避免因短暂的问题而造成爬取失败,自定义重试次数。 +- **➡️ 轮换代理** - 配合失败重试,自定义错误次数以及 HTTP 状态码自动轮换代理。 +- **👀 设备指纹** - 零配置或自定义配置,避免指纹识别从不同位置识别并跟踪我们。 - **🚀 优先队列** - 根据单个爬取目标的优先级可以优先于其他目标提前爬取。 - **☁️ 爬取 SPA** - 爬取 SPA(单页应用程序)生成预渲染内容(即“SSR”(服务器端渲染))。 - **⚒️ 控制页面** - 可以表单提交、键盘输入、事件操作、生成页面的屏幕截图等。 -- **🧾 捕获记录** - 对爬取的信息进行捕获记录,并在控制台进行高亮的提醒。 +- **🧾 捕获记录** - 对爬取进行捕获记录,并在终端使用彩色字符串提醒。 - **🦾 TypeScript** - 拥有类型,通过泛型实现完整的类型。 ## 跟 puppeteer 的关系 -crawlPage API 内置了 [puppeteer](https://github.com/puppeteer/puppeteer) ,您只需要传入一些配置选项即可完成一些操作,结果会将 Brower 实例和 Page 实例暴露出来。 +crawlPage API 内置了 [puppeteer](https://github.com/puppeteer/puppeteer) ,您只需要传入一些配置选项即可完成一些操作,结果会将 Brower 实例和 Page 实例暴露出来,您拿到的 Brower 实例和 Page 实例将是完好的,x-crawl 并不会对其重写。 # 目录 @@ -48,9 +49,10 @@ crawlPage API 内置了 [puppeteer](https://github.com/puppeteer/puppeteer) , - [onBeforeSaveItemFile](#onBeforeSaveItemFile) - [启动轮询](#启动轮询) - [配置优先级](#配置优先级) - - [自定义设备指纹](#自定义设备指纹) - [间隔时间](#间隔时间) - [失败重试](#失败重试) + - [轮换代理](#轮换代理) + - [自定义设备指纹](#自定义设备指纹) - [优先队列](#优先队列) - [关于结果](#关于结果) - [TypeScript](#TypeScript) @@ -88,19 +90,18 @@ crawlPage API 内置了 [puppeteer](https://github.com/puppeteer/puppeteer) , - [类型](#类型-6) - [API Config](#API-config) - [XCrawlConfig](#XCrawlConfig) - - [Detail target config](#Detail-target-config) + - [Detail Target Config](#Detail-Target-Config) - [CrawlPageDetailTargetConfig](#CrawlPageDetailTargetConfig) - [CrawlDataDetailTargetConfig](#CrawlDataDetailTargetConfig) - [CrawlFileDetailTargetConfig](#CrawlFileDetailTargetConfig) - - [Advanced config](#Advanced-config) + - [Advanced Config](#Advanced-Config) - [CrawlPageAdvancedConfig](#CrawlPageAdvancedConfig) - [CrawlDataAdvancedConfig](#CrawlDataAdvancedConfig) - [CrawlFileAdvancedConfig](#CrawlFileAdvancedConfig) - [StartPollingConfig](#StartPollingConfig) - - [Crawl other config](#Crawl-other-config) + - [Crawl Other Config](#Crawl-Other-Config) - [CrawlCommonConfig](#CrawlCommonConfig) - [DetailTargetFingerprintCommon](#DetailTargetFingerprintCommon) - - [AdvancedFingerprintCommon](#AdvancedFingerprintCommon) - [Mobile](#Mobile) - [Platform](#Platform) - [PageCookies](#PageCookies) @@ -108,13 +109,15 @@ crawlPage API 内置了 [puppeteer](https://github.com/puppeteer/puppeteer) , - [IntervalTime](#IntervalTime) - [API Result](#API-Result) - [XCrawlInstance](#XCrawlInstance) - - [CrawlCommonRes](#CrawlCommonRes) - - [CrawlPageSingleRes](#CrawlPageSingleRes) - - [CrawlDataSingleRes](#CrawlDataSingleRes) - - [CrawlFileSingleRes](#CrawlFileSingleRes) + - [CrawlCommonResult](#CrawlCommonResult) + - [CrawlPageSingleResult](#CrawlPageSingleResult) + - [CrawlDataSingleResult](#CrawlDataSingleResult) + - [CrawlFileSingleResult](#CrawlFileSingleResult) - [API Other](#API-Other) - [AnyObject](#AnyObject) - [更多](#更多) + - [社区](#社区) + - [Issues](#Issues) ## 安装 @@ -126,7 +129,7 @@ npm install x-crawl ## 示例 -每天自动获取 bilibili 首页、国漫、电影这三个页面的轮播图片为例: +以每天自动获取世界各地的经历和房间的一些照片为例: ```js // 1.导入模块 ES/CJS @@ -138,23 +141,31 @@ const myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } }) // 3.设置爬取任务 // 调用 startPolling API 开始轮询功能,每隔一天会调用回调函数 myXCrawl.startPolling({ d: 1 }, async (count, stopPolling) => { - // 调用 crawlPage API 爬取 首页、国漫、电影 这三个页面 - const res = await myXCrawl.crawlPage([ - 'https://www.bilibili.com', - 'https://www.bilibili.com/guochuang', - 'https://www.bilibili.com/movie' - ]) + // 调用 crawlPage API 来爬取页面 + const res = await myXCrawl.crawlPage({ + targets: [ + 'https://www.airbnb.cn/s/experiences', + 'https://www.airbnb.cn/s/plus_homes' + ], + viewport: { width: 1920, height: 1080 } + }) // 存放图片 URL 到 targets const targets = [] - const elSelectorMap = ['.carousel-inner', '.chief-recom-item', '.bg-item'] + const elSelectorMap = ['._fig15y', '._aov0j6'] for (const item of res) { const { id } = item const { page } = item.data - // 获取页面轮播图片元素的 URL - const urls = await page.$$eval(`${elSelectorMap[id - 1]} img`, (imgEls) => - imgEls.map((item) => item.src) + // 等待页面加载完成 + await new Promise((r) => setTimeout(r, 300)) + + // 获取页面图片的 URL + const urls = await page!.$$eval( + `${elSelectorMap[id - 1]} img`, + (imgEls) => { + return imgEls.map((item) => item.src) + } ) targets.push(...urls) @@ -303,7 +314,7 @@ myXCrawl.crawlPage('https://www.example.com').then(async (res) => { crawlPage API 拥有的声明周期函数: -- onCrawlItemComplete: 当每个爬取目标完成并进行了处理后会回调 +- onCrawlItemComplete: 当每个爬取目标完成后会回调 ##### onCrawlItemComplete @@ -339,7 +350,7 @@ myXCrawl.crawlData({ targets }).then((res) => { crawlData API 拥有的声明周期函数: -- onCrawlItemComplete: 当每个爬取目标完成并进行了处理后会回调 +- onCrawlItemComplete: 当每个爬取目标完成后会回调 ##### onCrawlItemComplete @@ -369,7 +380,7 @@ myXCrawl crawlFile API 拥有的声明周期函数: -- onCrawlItemComplete: 当每个爬取目标完成并进行了处理后会回调 +- onCrawlItemComplete: 当每个爬取目标完成后会回调 - onBeforeSaveItemFile: 会在保存文件前回调 @@ -450,72 +461,44 @@ myXCrawl.startPolling({ h: 2, m: 30 }, async (count, stopPolling) => { import xCrawl from 'x-crawl' // 应用实例配置 -const myXCrawl = xCrawl({ - intervalTime: { max: 3000, min: 1000 } +const testXCrawl = xCrawl({ + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } }) // 进阶配置 -myXCrawl.crawlPage({ - targets: [ - 'https://www.example.com/page-1', - { - // 详细目标配置 - url: 'https://www.example.com/page-1', - viewport: { width: 1920, height: 1080 } - } - ], - intervalTime: 1000, - viewport: { width: 800, height: 600 } -}) -``` - -在上面的实例中,**应用实例配置**和**进阶配置**中都设置了间隔时间,那么将会以**进阶配置**的间隔时间为准。在**进阶配置**和**详细目标配置**中设置了视口,那么第二个目标会以其**详细目标配置**的视口为准。 - -### 自定义设备指纹 - -自定义配置设备指纹,可避免通过指纹识别从不同位置识别并跟踪我们。 - -可以通过进阶用法在 fingerprint 传入多个信息,内部会帮助您随机分配给 targets 的每个目标。也可以直接用详细目标配置为目标设置特定的指纹。 - -以 crawlPage 为例: - -```js -import xCrawl from 'x-crawl' - -const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) - -myXCrawl +testXCrawl .crawlPage({ targets: [ 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + // 详细目标配置 { - // 指定指纹 - url: 'https://www.example.com/page-2', - fingerprint: { - maxWidth: 1980, - minWidth: 1200, - maxHeight: 1080, - minHidth: 800, - platform: 'Android' - } + url: 'https://www.example.com/page-3', + proxy: { urls: ['https://www.example.com/proxy-5'] } } ], - fingerprint: { - // 为 targets 里的每个目标设置指纹 - maxWidth: 1980, - maxHeight: 1080, - userAgents: [ - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36', - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0' + maxRetry: 10, + proxy: { + urls: [ + 'https://www.example.com/proxy-3', + 'https://www.example.com/proxy-4' ], - platforms: ['Chromium OS', 'iOS', 'Linux', 'macOS', 'Windows'] + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] } }) .then((res) => {}) ``` -更多指纹选项可以前往对应的配置查看。 +在上面的实例中,**应用实例配置**、**进阶配置**以及**详细目标配置**中都设置了**代理**,page3 将会采用自己的代理配置,page1 和 page2 将采用进阶配置的代理配置。 ### 间隔时间 @@ -547,6 +530,8 @@ intervalTime 选项默认为 undefined 。若有设置值,则会在爬取目 可避免因一时问题而造成爬取失败,将会等待这一轮爬取目标结束后重新爬取目标。 +可以在 创建爬虫应用实例、进阶用法、详细目标 这三个地方设置。 + ```js import xCrawl from 'x-crawl' @@ -559,6 +544,169 @@ myXCrawl maxRetry 属性决定要重试几次。 +### 轮换代理 + +配合失败重试,自定义错误次数以及 HTTP 状态码为爬取目标自动轮换代理。 + +可以在 创建爬虫应用实例、进阶用法、详细目标 这三个地方设置。 + +以 crawlPage 为例: + +```js +import xCrawl from 'x-crawl' + +const testXCrawl = xCrawl() + +testXCrawl + .crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + 'https://www.example.com/page-4', + // 为此目标取消代理 + { url: 'https://www.example.com/page-6', proxy: null }, + // 为此目标单独设置代理 + { + url: 'https://www.example.com/page-6', + proxy: { + urls: [ + 'https://www.example.com/proxy-4', + 'https://www.example.com/proxy-5' + ], + switchByErrorCount: 3 + } + } + ], + maxRetry: 10, + // 为此次的目标统一设置代理 + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } + }) + .then((res) => {}) +``` + +**注意:** 该功能需要配合失败重试才能正常使用。 + +### 自定义设备指纹 + +自定义配置设备指纹,可避免通过指纹识别从不同位置识别并跟踪我们。 + +可以通过进阶用法在 fingerprints 传入多个信息,内部会帮助您随机分配给 targets 的每个目标。也可以直接用详细目标配置为目标设置特定的指纹。 + +以 crawlPage 为例: + +```js +import xCrawl from 'x-crawl' + +const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) + +myXCrawl.crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + // 为此目标取消指纹 + { url: 'https://www.example.com/page-4', fingerprint: null }, + // 为此目标单独设置指纹 + { + url: 'https://www.example.com/page-5', + fingerprint: { + mobile: 'random', + platform: 'Windows', + acceptLanguage: `zh-CN,zh;q=0.9,en;q=0.8`, + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { name: 'Chrome', maxMinorVersion: 10, maxPatchVersion: 5615 }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 } + ] + } + } + } + ], + // 为此次的目标统一设置指纹 + fingerprints: [ + // 设备指纹 1 + { + maxWidth: 1024, + maxHeight: 800, + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + // 浏览器版本 + maxMajorVersion: 112, + minMajorVersion: 100, + maxMinorVersion: 20, + maxPatchVersion: 5000 + }, + { + name: 'Safari', + maxMajorVersion: 537, + minMajorVersion: 500, + maxMinorVersion: 36, + maxPatchVersion: 5000 + } + ] + } + }, + // 设备指纹 2 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 91, + minMajorVersion: 88, + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 }, + { name: 'Edg', maxMinorVersion: 10, maxPatchVersion: 864 } + ] + } + }, + // 设备指纹 3 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', + versions: [ + { + name: 'Firefox', + maxMajorVersion: 47, + minMajorVersion: 43, + maxMinorVersion: 10, + maxPatchVersion: 5000 + } + ] + } + } + ] +}) +``` + +更多指纹选项可以前往对应的配置查看。 + ### 优先队列 优先队列可以让某个爬取目标优先发送。 @@ -587,6 +735,7 @@ priority 属性的值越大就在当前爬取队列中越优先。 - isSuccess:是否成功爬取 - maxRetry:该次爬取目标的最大重试次数 - retryCount:该次爬取目标已经重试的次数 +- proxyDetails:记录代理情况 - crawlErrorQueue:该次爬取目标的报错收集 - data:该次爬取目标的爬取数据 @@ -647,23 +796,23 @@ crawlPage API 是一个函数。类型是 [重载函数](https://www.typescriptl type crawlPage = { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } ``` @@ -674,7 +823,7 @@ type crawlPage = { **返回值类型:** -- 查看 [CrawlPageSingleRes](#CrawlPageSingleRes) 类型 +- 查看 [CrawlPageSingleResult](#CrawlPageSingleResult) 类型 #### 示例 @@ -798,23 +947,23 @@ crawlData API 是一个函数。类型是 [重载函数](https://www.typescriptl type crawlData = { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } ``` @@ -825,7 +974,7 @@ type crawlData = { **返回值类型:** -- 查看 [CrawlDataSingleRes](#CrawlDataSingleRes) 类型 +- 查看 [CrawlDataSingleResult](#CrawlDataSingleResult) 类型 #### 示例 @@ -956,18 +1105,18 @@ crawlFile API 是一个函数。类型是 [重载函数](https://www.typescriptl type crawlFile = { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } ``` @@ -978,7 +1127,7 @@ type crawlFile = { **返回值类型:** -- 查看 [CrawlFileSingleRes](#CrawlFileSingleRes) 类型 +- 查看 [CrawlFileSingleResult](#CrawlFileSingleResult) 类型 #### 示例 @@ -1119,7 +1268,7 @@ myXCrawl.startPolling({ h: 2, m: 30 }, (count, stopPolling) => { ## 类型 -### API config +### API Config #### XCrawlConfig @@ -1143,7 +1292,7 @@ export interface XCrawlConfig extends CrawlCommonConfig { - intervalTime: undefined - crawlPage: undefined -#### Detail target config +#### Detail Target Config ##### CrawlPageDetailTargetConfig @@ -1156,9 +1305,9 @@ export interface CrawlPageDetailTargetConfig extends CrawlCommonConfig { viewport?: Viewport | null // puppeteer fingerprint?: | (DetailTargetFingerprintCommon & { - maxWidth: number + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number }) | null @@ -1222,7 +1371,7 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { - extension: string - fingerprint: undefined -#### Advanced config +#### Advanced Config ##### CrawlPageAdvancedConfig @@ -1230,18 +1379,18 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlPageDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon & { - maxWidth: number + fingerprints?: (DetailTargetFingerprintCommon & { + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number - } + })[] headers?: AnyObject cookies?: PageCookies - viewport?: Viewport // puppeteer + viewport?: Viewport - onCrawlItemComplete?: (crawlPageSingleRes: CrawlPageSingleRes) => void + onCrawlItemComplete?: (crawlPageSingleResult: CrawlPageSingleResult) => void } ``` @@ -1249,7 +1398,7 @@ export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - cookies: undefined - viewport: undefined @@ -1261,11 +1410,13 @@ export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlDataDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject - onCrawlItemComplete?: (crawlDataSingleRes: CrawlDataSingleRes) => void + onCrawlItemComplete?: ( + crawlDataSingleResult: CrawlDataSingleResult + ) => void } ``` @@ -1273,7 +1424,7 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - onCrawlItemComplete: undefined @@ -1283,13 +1434,13 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlFileDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject storeDir?: string extension?: string - onCrawlItemComplete?: (crawlFileSingleRes: CrawlFileSingleRes) => void + onCrawlItemComplete?: (crawlFileSingleResult: CrawlFileSingleResult) => void onBeforeSaveItemFile?: (info: { id: number fileName: string @@ -1303,7 +1454,7 @@ export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - storeDir: \_\_dirname - extension: string @@ -1326,15 +1477,19 @@ export interface StartPollingConfig { - h: undefined - m: undefined -#### Crawl other config +#### Crawl Other Config ##### CrawlCommonConfig ```ts export interface CrawlCommonConfig { - timeout?: number - proxy?: string - maxRetry?: number + timeout?: number | null + proxy?: { + urls: string[] + switchByHttpStatus?: number[] + switchByErrorCount?: number + } | null + maxRetry?: number | null } ``` @@ -1348,45 +1503,34 @@ export interface CrawlCommonConfig { ```ts export interface DetailTargetFingerprintCommon { - userAgent?: string ua?: string + mobile?: '?0' | '?1' | 'random' platform?: Platform platformVersion?: string - mobile?: Mobile acceptLanguage?: string + userAgent?: { + value: string + versions?: { + name: string + maxMajorVersion?: number + minMajorVersion?: number + maxMinorVersion?: number + minMinorVersion?: number + maxPatchVersion?: number + minPatchVersion?: number + }[] + } } ``` **默认值** -- userAgent: undefined - ua: undefined +- mobile: undefined - platform: undefined - platformVersion: undefined -- mobile: undefined - acceptLanguage: undefined - -##### AdvancedFingerprintCommon - -```ts -export interface AdvancedFingerprintCommon { - userAgents?: string[] - uas?: string[] - platforms?: Platform[] - platformVersions?: string[] - mobiles?: Mobile[] - acceptLanguages?: string[] -} -``` - -**默认值** - -- userAgents: undefined -- uas: undefined -- platforms: undefined -- platformVersions: undefined -- mobiles: undefined -- acceptLanguages: undefined +- userAgent: undefined ##### Mobile @@ -1449,7 +1593,7 @@ export type Method = export type IntervalTime = number | { max: number; min?: number } ``` -### API result +### API Result #### XCrawlInstance @@ -1458,62 +1602,62 @@ export interface XCrawlInstance { crawlPage: { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } crawlData: { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } crawlFile: { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } startPolling: ( @@ -1523,14 +1667,15 @@ export interface XCrawlInstance { } ``` -#### CrawlCommonRes +#### CrawlCommonResult ```ts -export interface CrawlCommonRes { +export interface CrawlCommonResult { id: number isSuccess: boolean maxRetry: number retryCount: number + proxyDetails: ProxyDetails crawlErrorQueue: Error[] } ``` @@ -1539,12 +1684,13 @@ export interface CrawlCommonRes { - isSuccess:是否成功爬取 - maxRetry:该次爬取目标的最大重试次数 - retryCount:该次爬取目标已经重试的次数 +- proxyDetails:记录代理情况 - crawlErrorQueue:该次爬取目标的报错收集 -#### CrawlPageSingleRes +#### CrawlPageSingleResult ```ts -export interface CrawlPageSingleRes extends CrawlCommonRes { +export interface CrawlPageSingleResult extends CrawlCommonResult { data: { browser: Browser // puppeteer response: HTTPResponse | null // puppeteer @@ -1553,10 +1699,10 @@ export interface CrawlPageSingleRes extends CrawlCommonRes { } ``` -#### CrawlDataSingleRes +#### CrawlDataSingleResult ```ts -export interface CrawlDataSingleRes extends CrawlCommonRes { +export interface CrawlDataSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined headers: IncomingHttpHeaders // nodejs http @@ -1565,10 +1711,10 @@ export interface CrawlDataSingleRes extends CrawlCommonRes { } ``` -#### CrawlFileSingleRes +#### CrawlFileSingleResult ```ts -export interface CrawlFileSingleRes extends CrawlCommonRes { +export interface CrawlFileSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined headers: IncomingHttpHeaders // nodejs http @@ -1596,6 +1742,10 @@ export interface AnyObject extends Object { ## 更多 -如果您有 **问题 、需求、好的建议** 请在 https://github.com/coder-hxl/x-crawl/issues 中提 **Issues** 。 +### 社区 + +**GitHub Discussions:** 可以通过 [GitHub Discussions](https://github.com/coder-hxl/x-crawl/discussions) 进行讨论。 + +### Issues -感谢大家的支持。 +如果您有 **问题 、需求、好的建议** 可以在 [GitHub Issues](https://github.com/coder-hxl/x-crawl/issues) 中提 **Issues** 。 diff --git a/jest.config.js b/jest.config.js index adeab3b7..24686a18 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,5 +3,9 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', collectCoverage: true, - detectOpenHandles: true + detectOpenHandles: true, + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + '^publish/(.*)$': '/publish/$1' + } } diff --git a/package.json b/package.json index eaf93e6a..111f6ae0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "x-crawl", - "version": "6.0.1", + "version": "7.0.0", "author": "coderHXL", "description": "x-crawl is a flexible Node.js multifunctional crawler library.", "license": "MIT", @@ -22,11 +22,12 @@ "build-dts": "tsc && prettier --write ./publish/src", "build-strict": "pnpm test-dev && pnpm build && pnpm test-pro", "start": "rollup --config script/start.mjs", + "start-server": "rollup --config script/server.mjs", "test-dev": "jest test/environment/test.ts dev", "test-pro": "jest test/environment/test.ts pro", - "test-crawlPage": "jest test/environment/crawlPage.test.ts dev", - "test-crawlData": "jest test/environment/crawlData.test.ts dev", - "test-crawlFile": "jest test/environment/crawlFile.test.ts dev", + "test-crawlPage": "jest test/environment/api/crawlPage.test.ts dev", + "test-crawlData": "jest test/environment/api/crawlData.test.ts dev", + "test-crawlFile": "jest test/environment/api/crawlFile.test.ts dev", "prettier": "prettier --write ." }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55dd1e15..c35ba264 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,54 +1,72 @@ -lockfileVersion: 5.4 - -specifiers: - '@babel/core': ^7.20.12 - '@babel/preset-env': ^7.20.2 - '@jest/globals': ^29.3.1 - '@rollup/plugin-babel': ^6.0.3 - '@rollup/plugin-run': ^3.0.1 - '@rollup/plugin-terser': ^0.4.0 - '@types/node': ^18.11.18 - '@typescript-eslint/eslint-plugin': ^5.48.2 - '@typescript-eslint/parser': ^5.48.2 - chalk: 4.1.2 - eslint: ^8.32.0 - https-proxy-agent: ^5.0.1 - jest: ^29.3.1 - prettier: ^2.8.3 - puppeteer: 19.10.0 - rollup: ^3.10.1 - rollup-plugin-typescript2: ^0.34.1 - ts-jest: ^29.0.5 - typescript: 5.0.2 - x-crawl: 'link:' +lockfileVersion: '6.0' dependencies: - chalk: 4.1.2 - https-proxy-agent: 5.0.1 - puppeteer: 19.10.0_typescript@5.0.2 - x-crawl: 'link:' + chalk: + specifier: 4.1.2 + version: 4.1.2 + https-proxy-agent: + specifier: ^5.0.1 + version: 5.0.1 + puppeteer: + specifier: 19.10.0 + version: 19.10.0(typescript@5.0.2) + x-crawl: + specifier: 'link:' + version: 'link:' devDependencies: - '@babel/core': 7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@jest/globals': 29.3.1 - '@rollup/plugin-babel': 6.0.3_j5gl3ppangg6ywsqfpneorhloy - '@rollup/plugin-run': 3.0.1_rollup@3.10.1 - '@rollup/plugin-terser': 0.4.0_rollup@3.10.1 - '@types/node': 18.11.18 - '@typescript-eslint/eslint-plugin': 5.48.2_xcchhpd3ezyqg4jn72wfkiwhri - '@typescript-eslint/parser': 5.48.2_pydb3hde2stcjsf742jarbqbry - eslint: 8.32.0 - jest: 29.3.1_@types+node@18.11.18 - prettier: 2.8.3 - rollup: 3.10.1 - rollup-plugin-typescript2: 0.34.1_xzkd7boxfqp7uxzhulfcfesazm - ts-jest: 29.0.5_oi7nmolcnmyqply4ewqwuryc6q - typescript: 5.0.2 + '@babel/core': + specifier: ^7.20.12 + version: 7.20.12 + '@babel/preset-env': + specifier: ^7.20.2 + version: 7.20.2(@babel/core@7.20.12) + '@jest/globals': + specifier: ^29.3.1 + version: 29.3.1 + '@rollup/plugin-babel': + specifier: ^6.0.3 + version: 6.0.3(@babel/core@7.20.12)(rollup@3.10.1) + '@rollup/plugin-run': + specifier: ^3.0.1 + version: 3.0.1(rollup@3.10.1) + '@rollup/plugin-terser': + specifier: ^0.4.0 + version: 0.4.0(rollup@3.10.1) + '@types/node': + specifier: ^18.11.18 + version: 18.11.18 + '@typescript-eslint/eslint-plugin': + specifier: ^5.48.2 + version: 5.48.2(@typescript-eslint/parser@5.48.2)(eslint@8.32.0)(typescript@5.0.2) + '@typescript-eslint/parser': + specifier: ^5.48.2 + version: 5.48.2(eslint@8.32.0)(typescript@5.0.2) + eslint: + specifier: ^8.32.0 + version: 8.32.0 + jest: + specifier: ^29.3.1 + version: 29.3.1(@types/node@18.11.18) + prettier: + specifier: ^2.8.3 + version: 2.8.3 + rollup: + specifier: ^3.10.1 + version: 3.10.1 + rollup-plugin-typescript2: + specifier: ^0.34.1 + version: 0.34.1(rollup@3.10.1)(typescript@5.0.2) + ts-jest: + specifier: ^29.0.5 + version: 29.0.5(@babel/core@7.20.12)(jest@29.3.1)(typescript@5.0.2) + typescript: + specifier: 5.0.2 + version: 5.0.2 packages: - /@ampproject/remapping/2.2.0: + /@ampproject/remapping@2.2.0: resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} engines: {node: '>=6.0.0'} dependencies: @@ -56,25 +74,25 @@ packages: '@jridgewell/trace-mapping': 0.3.17 dev: true - /@babel/code-frame/7.18.6: + /@babel/code-frame@7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 - /@babel/compat-data/7.20.10: + /@babel/compat-data@7.20.10: resolution: {integrity: sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==} engines: {node: '>=6.9.0'} dev: true - /@babel/core/7.20.12: + /@babel/core@7.20.12: resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 '@babel/generator': 7.20.7 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-module-transforms': 7.20.11 '@babel/helpers': 7.20.13 '@babel/parser': 7.20.13 @@ -90,7 +108,7 @@ packages: - supports-color dev: true - /@babel/generator/7.20.7: + /@babel/generator@7.20.7: resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==} engines: {node: '>=6.9.0'} dependencies: @@ -99,14 +117,14 @@ packages: jsesc: 2.5.2 dev: true - /@babel/helper-annotate-as-pure/7.18.6: + /@babel/helper-annotate-as-pure@7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-builder-binary-assignment-operator-visitor/7.18.9: + /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} engines: {node: '>=6.9.0'} dependencies: @@ -114,7 +132,7 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: + /@babel/helper-compilation-targets@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -128,7 +146,7 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-create-class-features-plugin/7.20.12_@babel+core@7.20.12: + /@babel/helper-create-class-features-plugin@7.20.12(@babel/core@7.20.12): resolution: {integrity: sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -147,7 +165,7 @@ packages: - supports-color dev: true - /@babel/helper-create-regexp-features-plugin/7.20.5_@babel+core@7.20.12: + /@babel/helper-create-regexp-features-plugin@7.20.5(@babel/core@7.20.12): resolution: {integrity: sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==} engines: {node: '>=6.9.0'} peerDependencies: @@ -158,13 +176,13 @@ packages: regexpu-core: 5.2.2 dev: true - /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12: + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.20.12): resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 debug: 4.3.4 lodash.debounce: 4.0.8 @@ -174,19 +192,19 @@ packages: - supports-color dev: true - /@babel/helper-environment-visitor/7.18.9: + /@babel/helper-environment-visitor@7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-explode-assignable-expression/7.18.6: + /@babel/helper-explode-assignable-expression@7.18.6: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-function-name/7.19.0: + /@babel/helper-function-name@7.19.0: resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} engines: {node: '>=6.9.0'} dependencies: @@ -194,28 +212,28 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/helper-hoist-variables/7.18.6: + /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-member-expression-to-functions/7.20.7: + /@babel/helper-member-expression-to-functions@7.20.7: resolution: {integrity: sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-module-imports/7.18.6: + /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-module-transforms/7.20.11: + /@babel/helper-module-transforms@7.20.11: resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} engines: {node: '>=6.9.0'} dependencies: @@ -231,19 +249,19 @@ packages: - supports-color dev: true - /@babel/helper-optimise-call-expression/7.18.6: + /@babel/helper-optimise-call-expression@7.18.6: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-plugin-utils/7.20.2: + /@babel/helper-plugin-utils@7.20.2: resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.12: + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -258,7 +276,7 @@ packages: - supports-color dev: true - /@babel/helper-replace-supers/7.20.7: + /@babel/helper-replace-supers@7.20.7: resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} engines: {node: '>=6.9.0'} dependencies: @@ -272,42 +290,42 @@ packages: - supports-color dev: true - /@babel/helper-simple-access/7.20.2: + /@babel/helper-simple-access@7.20.2: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-skip-transparent-expression-wrappers/7.20.0: + /@babel/helper-skip-transparent-expression-wrappers@7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-split-export-declaration/7.18.6: + /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.7 dev: true - /@babel/helper-string-parser/7.19.4: + /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-identifier/7.19.1: + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option/7.18.6: + /@babel/helper-validator-option@7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-wrap-function/7.20.5: + /@babel/helper-wrap-function@7.20.5: resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} engines: {node: '>=6.9.0'} dependencies: @@ -319,7 +337,7 @@ packages: - supports-color dev: true - /@babel/helpers/7.20.13: + /@babel/helpers@7.20.13: resolution: {integrity: sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==} engines: {node: '>=6.9.0'} dependencies: @@ -330,7 +348,7 @@ packages: - supports-color dev: true - /@babel/highlight/7.18.6: + /@babel/highlight@7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: @@ -338,7 +356,7 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 - /@babel/parser/7.20.13: + /@babel/parser@7.20.13: resolution: {integrity: sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==} engines: {node: '>=6.0.0'} hasBin: true @@ -346,7 +364,7 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.12: + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -356,7 +374,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.20.7_@babel+core@7.20.12: + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -365,10 +383,10 @@ packages: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12 + '@babel/plugin-proposal-optional-chaining': 7.20.7(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -377,40 +395,40 @@ packages: '@babel/core': 7.20.12 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.12) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-static-block/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-class-static-block@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.12) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -418,10 +436,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.12: + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -429,10 +447,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -440,10 +458,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-logical-assignment-operators/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: @@ -451,10 +469,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -462,10 +480,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -473,10 +491,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -484,13 +502,13 @@ packages: dependencies: '@babel/compat-data': 7.20.10 '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.20.12 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.20.7(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -498,10 +516,10 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-optional-chaining/7.20.7_@babel+core@7.20.12: + /@babel/plugin-proposal-optional-chaining@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -510,23 +528,23 @@ packages: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.12) dev: true - /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object/7.20.5_@babel+core@7.20.12: + /@babel/plugin-proposal-private-property-in-object@7.20.5(@babel/core@7.20.12): resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -534,25 +552,25 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.20.12_@babel+core@7.20.12 + '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.12) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.12: + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.20.12): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -561,7 +579,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -570,7 +588,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.12: + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.20.12): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -579,7 +597,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.20.12): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -589,7 +607,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -598,7 +616,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -607,7 +625,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.12: + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.20.12): resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -617,7 +635,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.12: + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.20.12): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -626,7 +644,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -635,7 +653,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.12: + /@babel/plugin-syntax-jsx@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -645,7 +663,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.12: + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.20.12): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -654,7 +672,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -663,7 +681,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.12: + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.20.12): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -672,7 +690,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -681,7 +699,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -690,7 +708,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.12: + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.20.12): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -699,7 +717,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.20.12): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -709,7 +727,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.12: + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.20.12): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -719,7 +737,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.12: + /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.20.12): resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -729,7 +747,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -739,7 +757,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-async-to-generator/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -748,12 +766,12 @@ packages: '@babel/core': 7.20.12 '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.20.12) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -763,7 +781,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-block-scoping@7.20.11(@babel/core@7.20.12): resolution: {integrity: sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -773,7 +791,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-classes/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-classes@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -781,7 +799,7 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 '@babel/helper-optimise-call-expression': 7.18.6 @@ -793,7 +811,7 @@ packages: - supports-color dev: true - /@babel/plugin-transform-computed-properties/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -804,7 +822,7 @@ packages: '@babel/template': 7.20.7 dev: true - /@babel/plugin-transform-destructuring/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-destructuring@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -814,18 +832,18 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -835,7 +853,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -846,7 +864,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.12: + /@babel/plugin-transform-for-of@7.18.8(@babel/core@7.20.12): resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -856,19 +874,19 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-function-name': 7.19.0 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -878,7 +896,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -888,7 +906,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.20.12): resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: @@ -901,7 +919,7 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-modules-commonjs@7.20.11(@babel/core@7.20.12): resolution: {integrity: sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -915,7 +933,7 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.20.12: + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.20.12): resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -930,7 +948,7 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -943,18 +961,18 @@ packages: - supports-color dev: true - /@babel/plugin-transform-named-capturing-groups-regex/7.20.5_@babel+core@7.20.12: + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.20.12): resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -964,7 +982,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -977,7 +995,7 @@ packages: - supports-color dev: true - /@babel/plugin-transform-parameters/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-parameters@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -987,7 +1005,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -997,7 +1015,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-regenerator/7.20.5_@babel+core@7.20.12: + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.20.12): resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1008,7 +1026,7 @@ packages: regenerator-transform: 0.15.1 dev: true - /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1018,7 +1036,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1028,7 +1046,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-spread/7.20.7_@babel+core@7.20.12: + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.20.12): resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1039,7 +1057,7 @@ packages: '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true - /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1049,7 +1067,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1059,7 +1077,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.12: + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.20.12): resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1069,7 +1087,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.12: + /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.20.12): resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1079,18 +1097,18 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.12: + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.20.12): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.20.5_@babel+core@7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/preset-env/7.20.2_@babel+core@7.20.12: + /@babel/preset-env@7.20.2(@babel/core@7.20.12): resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -1098,105 +1116,105 @@ packages: dependencies: '@babel/compat-data': 7.20.10 '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 + '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.20.12) '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-class-static-block': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-private-property-in-object': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.20.12 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.20.12 - '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-commonjs': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-regenerator': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.12 - '@babel/preset-modules': 0.1.5_@babel+core@7.20.12 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-class-static-block': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-optional-chaining': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-private-property-in-object': 7.20.5(@babel/core@7.20.12) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.12) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.20.12) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.12) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.20.12) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.12) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.20.12) + '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoping': 7.20.11(@babel/core@7.20.12) + '@babel/plugin-transform-classes': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-destructuring': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-for-of': 7.18.8(@babel/core@7.20.12) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.20.12) + '@babel/plugin-transform-modules-commonjs': 7.20.11(@babel/core@7.20.12) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.20.12) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.20.12) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.20.12) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.20.12) + '@babel/preset-modules': 0.1.5(@babel/core@7.20.12) '@babel/types': 7.20.7 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 - babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 - babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.20.12) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.20.12) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.20.12) core-js-compat: 3.27.2 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules/0.1.5_@babel+core@7.20.12: + /@babel/preset-modules@0.1.5(@babel/core@7.20.12): resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.20.12) '@babel/types': 7.20.7 esutils: 2.0.3 dev: true - /@babel/runtime/7.20.13: + /@babel/runtime@7.20.13: resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 dev: true - /@babel/template/7.20.7: + /@babel/template@7.20.7: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} dependencies: @@ -1205,7 +1223,7 @@ packages: '@babel/types': 7.20.7 dev: true - /@babel/traverse/7.20.13: + /@babel/traverse@7.20.13: resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==} engines: {node: '>=6.9.0'} dependencies: @@ -1223,7 +1241,7 @@ packages: - supports-color dev: true - /@babel/types/7.20.7: + /@babel/types@7.20.7: resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==} engines: {node: '>=6.9.0'} dependencies: @@ -1232,11 +1250,11 @@ packages: to-fast-properties: 2.0.0 dev: true - /@bcoe/v8-coverage/0.2.3: + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@eslint/eslintrc/1.4.1: + /@eslint/eslintrc@1.4.1: resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -1253,7 +1271,7 @@ packages: - supports-color dev: true - /@humanwhocodes/config-array/0.11.8: + /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} dependencies: @@ -1264,16 +1282,16 @@ packages: - supports-color dev: true - /@humanwhocodes/module-importer/1.0.1: + /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema/1.2.1: + /@humanwhocodes/object-schema@1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@istanbuljs/load-nyc-config/1.1.0: + /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} dependencies: @@ -1284,12 +1302,12 @@ packages: resolve-from: 5.0.0 dev: true - /@istanbuljs/schema/0.1.3: + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true - /@jest/console/29.3.1: + /@jest/console@29.3.1: resolution: {integrity: sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1301,7 +1319,7 @@ packages: slash: 3.0.0 dev: true - /@jest/core/29.3.1: + /@jest/core@29.3.1: resolution: {integrity: sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -1322,7 +1340,7 @@ packages: exit: 0.1.2 graceful-fs: 4.2.10 jest-changed-files: 29.2.0 - jest-config: 29.3.1_@types+node@18.15.10 + jest-config: 29.3.1(@types/node@18.15.10) jest-haste-map: 29.3.1 jest-message-util: 29.3.1 jest-regex-util: 29.2.0 @@ -1343,7 +1361,7 @@ packages: - ts-node dev: true - /@jest/environment/29.3.1: + /@jest/environment@29.3.1: resolution: {integrity: sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1353,14 +1371,14 @@ packages: jest-mock: 29.3.1 dev: true - /@jest/expect-utils/29.3.1: + /@jest/expect-utils@29.3.1: resolution: {integrity: sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-get-type: 29.2.0 dev: true - /@jest/expect/29.3.1: + /@jest/expect@29.3.1: resolution: {integrity: sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1370,7 +1388,7 @@ packages: - supports-color dev: true - /@jest/fake-timers/29.3.1: + /@jest/fake-timers@29.3.1: resolution: {integrity: sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1382,7 +1400,7 @@ packages: jest-util: 29.3.1 dev: true - /@jest/globals/29.3.1: + /@jest/globals@29.3.1: resolution: {integrity: sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1394,7 +1412,7 @@ packages: - supports-color dev: true - /@jest/reporters/29.3.1: + /@jest/reporters@29.3.1: resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -1431,14 +1449,14 @@ packages: - supports-color dev: true - /@jest/schemas/29.0.0: + /@jest/schemas@29.0.0: resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.24.51 dev: true - /@jest/source-map/29.2.0: + /@jest/source-map@29.2.0: resolution: {integrity: sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1447,7 +1465,7 @@ packages: graceful-fs: 4.2.10 dev: true - /@jest/test-result/29.3.1: + /@jest/test-result@29.3.1: resolution: {integrity: sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1457,7 +1475,7 @@ packages: collect-v8-coverage: 1.0.1 dev: true - /@jest/test-sequencer/29.3.1: + /@jest/test-sequencer@29.3.1: resolution: {integrity: sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1467,7 +1485,7 @@ packages: slash: 3.0.0 dev: true - /@jest/transform/29.3.1: + /@jest/transform@29.3.1: resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1490,7 +1508,7 @@ packages: - supports-color dev: true - /@jest/types/29.3.1: + /@jest/types@29.3.1: resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -1502,7 +1520,7 @@ packages: chalk: 4.1.2 dev: true - /@jridgewell/gen-mapping/0.1.1: + /@jridgewell/gen-mapping@0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} dependencies: @@ -1510,7 +1528,7 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@jridgewell/gen-mapping/0.3.2: + /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} dependencies: @@ -1519,35 +1537,35 @@ packages: '@jridgewell/trace-mapping': 0.3.17 dev: true - /@jridgewell/resolve-uri/3.1.0: + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/set-array/1.1.2: + /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/source-map/0.3.2: + /@jridgewell/source-map@0.3.2: resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} dependencies: '@jridgewell/gen-mapping': 0.3.2 '@jridgewell/trace-mapping': 0.3.17 dev: true - /@jridgewell/sourcemap-codec/1.4.14: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.17: + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@nodelib/fs.scandir/2.1.5: + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} dependencies: @@ -1555,12 +1573,12 @@ packages: run-parallel: 1.2.0 dev: true - /@nodelib/fs.stat/2.0.5: + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} dev: true - /@nodelib/fs.walk/1.2.8: + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} dependencies: @@ -1568,7 +1586,7 @@ packages: fastq: 1.15.0 dev: true - /@puppeteer/browsers/0.4.1_typescript@5.0.2: + /@puppeteer/browsers@0.4.1(typescript@5.0.2): resolution: {integrity: sha512-4IICvy1McAkT/HyNZHIs7sp8ngBX1dmO0TPQ+FWq9ATQMqI8p+Ulm5A3kS2wYDh5HDHHkYrrETOu6rlj64VuTw==} engines: {node: '>=14.1.0'} hasBin: true @@ -1591,7 +1609,7 @@ packages: - supports-color dev: false - /@rollup/plugin-babel/6.0.3_j5gl3ppangg6ywsqfpneorhloy: + /@rollup/plugin-babel@6.0.3(@babel/core@7.20.12)(rollup@3.10.1): resolution: {integrity: sha512-fKImZKppa1A/gX73eg4JGo+8kQr/q1HBQaCGKECZ0v4YBBv3lFqi14+7xyApECzvkLTHCifx+7ntcrvtBIRcpg==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1606,11 +1624,11 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-module-imports': 7.18.6 - '@rollup/pluginutils': 5.0.2_rollup@3.10.1 + '@rollup/pluginutils': 5.0.2(rollup@3.10.1) rollup: 3.10.1 dev: true - /@rollup/plugin-run/3.0.1_rollup@3.10.1: + /@rollup/plugin-run@3.0.1(rollup@3.10.1): resolution: {integrity: sha512-wbYR1Ahz8ohYnlyXzpBTwhGWfs+OO/uZMjgpDGr8AgnL/XfoTbO7BuNYY8ncR/j/1dhCJo+NDuTRkIeCxE434Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1623,7 +1641,7 @@ packages: rollup: 3.10.1 dev: true - /@rollup/plugin-terser/0.4.0_rollup@3.10.1: + /@rollup/plugin-terser@0.4.0(rollup@3.10.1): resolution: {integrity: sha512-Ipcf3LPNerey1q9ZMjiaWHlNPEHNU/B5/uh9zXLltfEQ1lVSLLeZSgAtTPWGyw8Ip1guOeq+mDtdOlEj/wNxQw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1638,7 +1656,7 @@ packages: terser: 5.16.1 dev: true - /@rollup/pluginutils/4.2.1: + /@rollup/pluginutils@4.2.1: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} dependencies: @@ -1646,7 +1664,7 @@ packages: picomatch: 2.3.1 dev: true - /@rollup/pluginutils/5.0.2_rollup@3.10.1: + /@rollup/pluginutils@5.0.2(rollup@3.10.1): resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1661,23 +1679,23 @@ packages: rollup: 3.10.1 dev: true - /@sinclair/typebox/0.24.51: + /@sinclair/typebox@0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true - /@sinonjs/commons/1.8.6: + /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: type-detect: 4.0.8 dev: true - /@sinonjs/fake-timers/9.1.2: + /@sinonjs/fake-timers@9.1.2: resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} dependencies: '@sinonjs/commons': 1.8.6 dev: true - /@types/babel__core/7.20.0: + /@types/babel__core@7.20.0: resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} dependencies: '@babel/parser': 7.20.13 @@ -1687,95 +1705,95 @@ packages: '@types/babel__traverse': 7.18.3 dev: true - /@types/babel__generator/7.6.4: + /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: '@babel/types': 7.20.7 dev: true - /@types/babel__template/7.4.1: + /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.20.13 '@babel/types': 7.20.7 dev: true - /@types/babel__traverse/7.18.3: + /@types/babel__traverse@7.18.3: resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} dependencies: '@babel/types': 7.20.7 dev: true - /@types/estree/1.0.0: + /@types/estree@1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} dev: true - /@types/graceful-fs/4.1.6: + /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: '@types/node': 18.15.10 dev: true - /@types/istanbul-lib-coverage/2.0.4: + /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} dev: true - /@types/istanbul-lib-report/3.0.0: + /@types/istanbul-lib-report@3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 dev: true - /@types/istanbul-reports/3.0.1: + /@types/istanbul-reports@3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/json-schema/7.0.11: + /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true - /@types/node/14.18.30: + /@types/node@14.18.30: resolution: {integrity: sha512-8OEyg4oc/CqN5+LbInKNLA8MfbGzbC+k8lVPePXazuwEVrVeQ9gwMDX00HJwWbC7syc1FWRU6Mow0Lm+mibHAQ==} dev: true - /@types/node/18.11.18: + /@types/node@18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /@types/node/18.15.10: + /@types/node@18.15.10: resolution: {integrity: sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==} dev: true - /@types/node/18.15.11: + /@types/node@18.15.11: resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} dev: false optional: true - /@types/prettier/2.7.2: + /@types/prettier@2.7.2: resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} dev: true - /@types/semver/7.3.13: + /@types/semver@7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@types/stack-utils/2.0.1: + /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true - /@types/yargs-parser/21.0.0: + /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true - /@types/yargs/17.0.20: + /@types/yargs@17.0.20: resolution: {integrity: sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A==} dependencies: '@types/yargs-parser': 21.0.0 dev: true - /@types/yauzl/2.10.0: + /@types/yauzl@2.10.0: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: @@ -1783,7 +1801,7 @@ packages: dev: false optional: true - /@typescript-eslint/eslint-plugin/5.48.2_xcchhpd3ezyqg4jn72wfkiwhri: + /@typescript-eslint/eslint-plugin@5.48.2(@typescript-eslint/parser@5.48.2)(eslint@8.32.0)(typescript@5.0.2): resolution: {integrity: sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1794,23 +1812,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.48.2_pydb3hde2stcjsf742jarbqbry + '@typescript-eslint/parser': 5.48.2(eslint@8.32.0)(typescript@5.0.2) '@typescript-eslint/scope-manager': 5.48.2 - '@typescript-eslint/type-utils': 5.48.2_pydb3hde2stcjsf742jarbqbry - '@typescript-eslint/utils': 5.48.2_pydb3hde2stcjsf742jarbqbry + '@typescript-eslint/type-utils': 5.48.2(eslint@8.32.0)(typescript@5.0.2) + '@typescript-eslint/utils': 5.48.2(eslint@8.32.0)(typescript@5.0.2) debug: 4.3.4 eslint: 8.32.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.2 + tsutils: 3.21.0(typescript@5.0.2) typescript: 5.0.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.48.2_pydb3hde2stcjsf742jarbqbry: + /@typescript-eslint/parser@5.48.2(eslint@8.32.0)(typescript@5.0.2): resolution: {integrity: sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1822,7 +1840,7 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.48.2 '@typescript-eslint/types': 5.48.2 - '@typescript-eslint/typescript-estree': 5.48.2_typescript@5.0.2 + '@typescript-eslint/typescript-estree': 5.48.2(typescript@5.0.2) debug: 4.3.4 eslint: 8.32.0 typescript: 5.0.2 @@ -1830,7 +1848,7 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager/5.48.2: + /@typescript-eslint/scope-manager@5.48.2: resolution: {integrity: sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -1838,7 +1856,7 @@ packages: '@typescript-eslint/visitor-keys': 5.48.2 dev: true - /@typescript-eslint/type-utils/5.48.2_pydb3hde2stcjsf742jarbqbry: + /@typescript-eslint/type-utils@5.48.2(eslint@8.32.0)(typescript@5.0.2): resolution: {integrity: sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1848,22 +1866,22 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.48.2_typescript@5.0.2 - '@typescript-eslint/utils': 5.48.2_pydb3hde2stcjsf742jarbqbry + '@typescript-eslint/typescript-estree': 5.48.2(typescript@5.0.2) + '@typescript-eslint/utils': 5.48.2(eslint@8.32.0)(typescript@5.0.2) debug: 4.3.4 eslint: 8.32.0 - tsutils: 3.21.0_typescript@5.0.2 + tsutils: 3.21.0(typescript@5.0.2) typescript: 5.0.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.48.2: + /@typescript-eslint/types@5.48.2: resolution: {integrity: sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.48.2_typescript@5.0.2: + /@typescript-eslint/typescript-estree@5.48.2(typescript@5.0.2): resolution: {integrity: sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1878,13 +1896,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.2 + tsutils: 3.21.0(typescript@5.0.2) typescript: 5.0.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.48.2_pydb3hde2stcjsf742jarbqbry: + /@typescript-eslint/utils@5.48.2(eslint@8.32.0)(typescript@5.0.2): resolution: {integrity: sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1894,17 +1912,17 @@ packages: '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.48.2 '@typescript-eslint/types': 5.48.2 - '@typescript-eslint/typescript-estree': 5.48.2_typescript@5.0.2 + '@typescript-eslint/typescript-estree': 5.48.2(typescript@5.0.2) eslint: 8.32.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.32.0 + eslint-utils: 3.0.0(eslint@8.32.0) semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys/5.48.2: + /@typescript-eslint/visitor-keys@5.48.2: resolution: {integrity: sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -1912,7 +1930,7 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /acorn-jsx/5.3.2_acorn@8.8.1: + /acorn-jsx@5.3.2(acorn@8.8.1): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1920,13 +1938,13 @@ packages: acorn: 8.8.1 dev: true - /acorn/8.8.1: + /acorn@8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /agent-base/6.0.2: + /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: @@ -1935,7 +1953,7 @@ packages: - supports-color dev: false - /ajv/6.12.6: + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: fast-deep-equal: 3.1.3 @@ -1944,35 +1962,35 @@ packages: uri-js: 4.4.1 dev: true - /ansi-escapes/4.3.2: + /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} dependencies: type-fest: 0.21.3 dev: true - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - /ansi-styles/3.2.1: + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - /ansi-styles/4.3.0: + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - /ansi-styles/5.2.0: + /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} dev: true - /anymatch/3.1.3: + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} dependencies: @@ -1980,21 +1998,21 @@ packages: picomatch: 2.3.1 dev: true - /argparse/1.0.10: + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 dev: true - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - /array-union/2.1.0: + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true - /babel-jest/29.3.1_@babel+core@7.20.12: + /babel-jest@29.3.1(@babel/core@7.20.12): resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -2004,7 +2022,7 @@ packages: '@jest/transform': 29.3.1 '@types/babel__core': 7.20.0 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.2.0_@babel+core@7.20.12 + babel-preset-jest: 29.2.0(@babel/core@7.20.12) chalk: 4.1.2 graceful-fs: 4.2.10 slash: 3.0.0 @@ -2012,7 +2030,7 @@ packages: - supports-color dev: true - /babel-plugin-istanbul/6.1.1: + /babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: @@ -2025,7 +2043,7 @@ packages: - supports-color dev: true - /babel-plugin-jest-hoist/29.2.0: + /babel-plugin-jest-hoist@29.2.0: resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -2035,63 +2053,63 @@ packages: '@types/babel__traverse': 7.18.3 dev: true - /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.12: + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.20.12): resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.20.10 '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.12) semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.12: + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.20.12): resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.12) core-js-compat: 3.27.2 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.12: + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.20.12): resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.12) transitivePeerDependencies: - supports-color dev: true - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.12: + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.20.12): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 - dev: true - - /babel-preset-jest/29.2.0_@babel+core@7.20.12: + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.12) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.20.12) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.20.12) + dev: true + + /babel-preset-jest@29.2.0(@babel/core@7.20.12): resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -2099,18 +2117,18 @@ packages: dependencies: '@babel/core': 7.20.12 babel-plugin-jest-hoist: 29.2.0 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.20.12) dev: true - /balanced-match/1.0.2: + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /base64-js/1.5.1: + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false - /bl/4.1.0: + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: buffer: 5.7.1 @@ -2118,21 +2136,21 @@ packages: readable-stream: 3.6.2 dev: false - /brace-expansion/1.1.11: + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 dev: true - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 dev: true - /browserslist/4.21.4: + /browserslist@4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2140,56 +2158,56 @@ packages: caniuse-lite: 1.0.30001446 electron-to-chromium: 1.4.284 node-releases: 2.0.8 - update-browserslist-db: 1.0.10_browserslist@4.21.4 + update-browserslist-db: 1.0.10(browserslist@4.21.4) dev: true - /bs-logger/0.2.6: + /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} dependencies: fast-json-stable-stringify: 2.1.0 dev: true - /bser/2.1.1: + /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 dev: true - /buffer-crc32/0.2.13: + /buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: false - /buffer-from/1.1.2: + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true - /buffer/5.7.1: + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 dev: false - /callsites/3.1.0: + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - /camelcase/5.3.1: + /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} dev: true - /camelcase/6.3.0: + /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001446: + /caniuse-lite@1.0.30001446: resolution: {integrity: sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==} dev: true - /chalk/2.4.2: + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} dependencies: @@ -2197,23 +2215,23 @@ packages: escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk/4.1.2: + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /char-regex/1.0.2: + /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} dev: true - /chownr/1.1.4: + /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} dev: false - /chromium-bidi/0.4.6_7yd6ibrwer4fxzjxd6md3toxiy: + /chromium-bidi@0.4.6(devtools-protocol@0.0.1107588): resolution: {integrity: sha512-TQOkWRaLI/IWvoP8XC+7jO4uHTIiAUiklXU1T0qszlUFEai9LgKXIBXy3pOS3EnQZ3bQtMbKUPkug0fTAEHCSw==} peerDependencies: devtools-protocol: '*' @@ -2222,16 +2240,16 @@ packages: mitt: 3.0.0 dev: false - /ci-info/3.7.1: + /ci-info@3.7.1: resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} engines: {node: '>=8'} dev: true - /cjs-module-lexer/1.2.2: + /cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true - /cliui/8.0.1: + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} dependencies: @@ -2239,59 +2257,59 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - /co/4.6.0: + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true - /collect-v8-coverage/1.0.1: + /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} dev: true - /color-convert/1.9.3: + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - /color-convert/2.0.1: + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - /color-name/1.1.3: + /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - /commander/2.20.3: + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true - /commondir/1.0.1: + /commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} dev: true - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /convert-source-map/1.9.0: + /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true - /convert-source-map/2.0.0: + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true - /core-js-compat/3.27.2: + /core-js-compat@3.27.2: resolution: {integrity: sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==} dependencies: browserslist: 4.21.4 dev: true - /cosmiconfig/8.1.3: + /cosmiconfig@8.1.3: resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==} engines: {node: '>=14'} dependencies: @@ -2301,7 +2319,7 @@ packages: path-type: 4.0.0 dev: false - /cross-fetch/3.1.5: + /cross-fetch@3.1.5: resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} dependencies: node-fetch: 2.6.7 @@ -2309,7 +2327,7 @@ packages: - encoding dev: false - /cross-spawn/7.0.3: + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: @@ -2318,7 +2336,7 @@ packages: which: 2.0.2 dev: true - /debug/4.3.4: + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -2329,89 +2347,89 @@ packages: dependencies: ms: 2.1.2 - /dedent/0.7.0: + /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true - /deep-is/0.1.4: + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge/4.2.2: + /deepmerge@4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} dev: true - /detect-newline/3.1.0: + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} dev: true - /devtools-protocol/0.0.1107588: + /devtools-protocol@0.0.1107588: resolution: {integrity: sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==} dev: false - /diff-sequences/29.3.1: + /diff-sequences@29.3.1: resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /dir-glob/3.0.1: + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: path-type: 4.0.0 dev: true - /doctrine/3.0.0: + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} dependencies: esutils: 2.0.3 dev: true - /electron-to-chromium/1.4.284: + /electron-to-chromium@1.4.284: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true - /emittery/0.13.1: + /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} dev: true - /emoji-regex/8.0.0: + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - /end-of-stream/1.4.4: + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 dev: false - /error-ex/1.3.2: + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 - /escalade/3.1.1: + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - /escape-string-regexp/1.0.5: + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - /escape-string-regexp/2.0.0: + /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} dev: true - /escape-string-regexp/4.0.0: + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /eslint-scope/5.1.1: + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} dependencies: @@ -2419,7 +2437,7 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope/7.1.1: + /eslint-scope@7.1.1: resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -2427,7 +2445,7 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.32.0: + /eslint-utils@3.0.0(eslint@8.32.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: @@ -2437,17 +2455,17 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /eslint-visitor-keys/2.1.0: + /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} dev: true - /eslint-visitor-keys/3.3.0: + /eslint-visitor-keys@3.3.0: resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.32.0: + /eslint@8.32.0: resolution: {integrity: sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true @@ -2463,7 +2481,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.32.0 + eslint-utils: 3.0.0(eslint@8.32.0) eslint-visitor-keys: 3.3.0 espree: 9.4.1 esquery: 1.4.0 @@ -2495,55 +2513,55 @@ packages: - supports-color dev: true - /espree/9.4.1: + /espree@9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.8.1 - acorn-jsx: 5.3.2_acorn@8.8.1 + acorn-jsx: 5.3.2(acorn@8.8.1) eslint-visitor-keys: 3.3.0 dev: true - /esprima/4.0.1: + /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true dev: true - /esquery/1.4.0: + /esquery@1.4.0: resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 dev: true - /esrecurse/4.3.0: + /esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} dependencies: estraverse: 5.3.0 dev: true - /estraverse/4.3.0: + /estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} dev: true - /estraverse/5.3.0: + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} dev: true - /estree-walker/2.0.2: + /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true - /esutils/2.0.3: + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} dev: true - /execa/5.1.1: + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} dependencies: @@ -2558,12 +2576,12 @@ packages: strip-final-newline: 2.0.0 dev: true - /exit/0.1.2: + /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} dev: true - /expect/29.3.1: + /expect@29.3.1: resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -2574,7 +2592,7 @@ packages: jest-util: 29.3.1 dev: true - /extract-zip/2.0.1: + /extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} hasBin: true @@ -2588,11 +2606,11 @@ packages: - supports-color dev: false - /fast-deep-equal/3.1.3: + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true - /fast-glob/3.2.12: + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} dependencies: @@ -2603,47 +2621,47 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify/2.1.0: + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true - /fast-levenshtein/2.0.6: + /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fastq/1.15.0: + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true - /fb-watchman/2.0.2: + /fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} dependencies: bser: 2.1.1 dev: true - /fd-slicer/1.1.0: + /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 dev: false - /file-entry-cache/6.0.1: + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: flat-cache: 3.0.4 dev: true - /fill-range/7.0.1: + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 dev: true - /find-cache-dir/3.3.2: + /find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} dependencies: @@ -2652,7 +2670,7 @@ packages: pkg-dir: 4.2.0 dev: true - /find-up/4.1.0: + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} dependencies: @@ -2660,7 +2678,7 @@ packages: path-exists: 4.0.0 dev: true - /find-up/5.0.0: + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} dependencies: @@ -2668,7 +2686,7 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache/3.0.4: + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: @@ -2676,15 +2694,15 @@ packages: rimraf: 3.0.2 dev: true - /flatted/3.2.7: + /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /fs-constants/1.0.0: + /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: false - /fs-extra/10.1.0: + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} dependencies: @@ -2693,11 +2711,11 @@ packages: universalify: 2.0.0 dev: true - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents/2.3.2: + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -2705,51 +2723,51 @@ packages: dev: true optional: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /gensync/1.0.0-beta.2: + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} dev: true - /get-caller-file/2.0.5: + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-package-type/0.1.0: + /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} dev: true - /get-stream/5.2.0: + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} dependencies: pump: 3.0.0 dev: false - /get-stream/6.0.1: + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} dev: true - /glob-parent/5.1.2: + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 dev: true - /glob-parent/6.0.2: + /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 dev: true - /glob/7.2.3: + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 @@ -2760,19 +2778,19 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/11.12.0: + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} dev: true - /globals/13.19.0: + /globals@13.19.0: resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: true - /globby/11.1.0: + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} dependencies: @@ -2784,34 +2802,34 @@ packages: slash: 3.0.0 dev: true - /graceful-fs/4.2.10: + /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true - /grapheme-splitter/1.0.4: + /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true - /has-flag/3.0.0: + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - /has-flag/4.0.0: + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /html-escaper/2.0.2: + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true - /https-proxy-agent/5.0.1: + /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} dependencies: @@ -2821,28 +2839,28 @@ packages: - supports-color dev: false - /human-signals/2.1.0: + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} dev: true - /ieee754/1.2.1: + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: false - /ignore/5.2.4: + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} dev: true - /import-fresh/3.3.0: + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-local/3.1.0: + /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} hasBin: true @@ -2851,76 +2869,76 @@ packages: resolve-cwd: 3.0.0 dev: true - /imurmurhash/0.1.4: + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /is-arrayish/0.2.1: + /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - /is-core-module/2.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} dev: true - /is-fullwidth-code-point/3.0.0: + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-generator-fn/2.1.0: + /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} dev: true - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: true - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true - /is-path-inside/3.0.3: + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true - /is-stream/2.0.1: + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} dev: true - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /istanbul-lib-coverage/3.2.0: + /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} dev: true - /istanbul-lib-instrument/5.2.1: + /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: @@ -2933,7 +2951,7 @@ packages: - supports-color dev: true - /istanbul-lib-report/3.0.0: + /istanbul-lib-report@3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} engines: {node: '>=8'} dependencies: @@ -2942,7 +2960,7 @@ packages: supports-color: 7.2.0 dev: true - /istanbul-lib-source-maps/4.0.1: + /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: @@ -2953,7 +2971,7 @@ packages: - supports-color dev: true - /istanbul-reports/3.1.5: + /istanbul-reports@3.1.5: resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} engines: {node: '>=8'} dependencies: @@ -2961,7 +2979,7 @@ packages: istanbul-lib-report: 3.0.0 dev: true - /jest-changed-files/29.2.0: + /jest-changed-files@29.2.0: resolution: {integrity: sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -2969,7 +2987,7 @@ packages: p-limit: 3.1.0 dev: true - /jest-circus/29.3.1: + /jest-circus@29.3.1: resolution: {integrity: sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -2996,7 +3014,7 @@ packages: - supports-color dev: true - /jest-cli/29.3.1_@types+node@18.11.18: + /jest-cli@29.3.1(@types/node@18.11.18): resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3013,7 +3031,7 @@ packages: exit: 0.1.2 graceful-fs: 4.2.10 import-local: 3.1.0 - jest-config: 29.3.1_@types+node@18.11.18 + jest-config: 29.3.1(@types/node@18.11.18) jest-util: 29.3.1 jest-validate: 29.3.1 prompts: 2.4.2 @@ -3024,7 +3042,7 @@ packages: - ts-node dev: true - /jest-config/29.3.1_@types+node@18.11.18: + /jest-config@29.3.1(@types/node@18.11.18): resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -3040,7 +3058,7 @@ packages: '@jest/test-sequencer': 29.3.1 '@jest/types': 29.3.1 '@types/node': 18.11.18 - babel-jest: 29.3.1_@babel+core@7.20.12 + babel-jest: 29.3.1(@babel/core@7.20.12) chalk: 4.1.2 ci-info: 3.7.1 deepmerge: 4.2.2 @@ -3063,7 +3081,7 @@ packages: - supports-color dev: true - /jest-config/29.3.1_@types+node@18.15.10: + /jest-config@29.3.1(@types/node@18.15.10): resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -3079,7 +3097,7 @@ packages: '@jest/test-sequencer': 29.3.1 '@jest/types': 29.3.1 '@types/node': 18.15.10 - babel-jest: 29.3.1_@babel+core@7.20.12 + babel-jest: 29.3.1(@babel/core@7.20.12) chalk: 4.1.2 ci-info: 3.7.1 deepmerge: 4.2.2 @@ -3102,7 +3120,7 @@ packages: - supports-color dev: true - /jest-diff/29.3.1: + /jest-diff@29.3.1: resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3112,14 +3130,14 @@ packages: pretty-format: 29.3.1 dev: true - /jest-docblock/29.2.0: + /jest-docblock@29.2.0: resolution: {integrity: sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: detect-newline: 3.1.0 dev: true - /jest-each/29.3.1: + /jest-each@29.3.1: resolution: {integrity: sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3130,7 +3148,7 @@ packages: pretty-format: 29.3.1 dev: true - /jest-environment-node/29.3.1: + /jest-environment-node@29.3.1: resolution: {integrity: sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3142,12 +3160,12 @@ packages: jest-util: 29.3.1 dev: true - /jest-get-type/29.2.0: + /jest-get-type@29.2.0: resolution: {integrity: sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-haste-map/29.3.1: + /jest-haste-map@29.3.1: resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3166,7 +3184,7 @@ packages: fsevents: 2.3.2 dev: true - /jest-leak-detector/29.3.1: + /jest-leak-detector@29.3.1: resolution: {integrity: sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3174,7 +3192,7 @@ packages: pretty-format: 29.3.1 dev: true - /jest-matcher-utils/29.3.1: + /jest-matcher-utils@29.3.1: resolution: {integrity: sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3184,7 +3202,7 @@ packages: pretty-format: 29.3.1 dev: true - /jest-message-util/29.3.1: + /jest-message-util@29.3.1: resolution: {integrity: sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3199,7 +3217,7 @@ packages: stack-utils: 2.0.6 dev: true - /jest-mock/29.3.1: + /jest-mock@29.3.1: resolution: {integrity: sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3208,7 +3226,7 @@ packages: jest-util: 29.3.1 dev: true - /jest-pnp-resolver/1.2.3_jest-resolve@29.3.1: + /jest-pnp-resolver@1.2.3(jest-resolve@29.3.1): resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} peerDependencies: @@ -3220,12 +3238,12 @@ packages: jest-resolve: 29.3.1 dev: true - /jest-regex-util/29.2.0: + /jest-regex-util@29.2.0: resolution: {integrity: sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-resolve-dependencies/29.3.1: + /jest-resolve-dependencies@29.3.1: resolution: {integrity: sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3235,14 +3253,14 @@ packages: - supports-color dev: true - /jest-resolve/29.3.1: + /jest-resolve@29.3.1: resolution: {integrity: sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 graceful-fs: 4.2.10 jest-haste-map: 29.3.1 - jest-pnp-resolver: 1.2.3_jest-resolve@29.3.1 + jest-pnp-resolver: 1.2.3(jest-resolve@29.3.1) jest-util: 29.3.1 jest-validate: 29.3.1 resolve: 1.22.1 @@ -3250,7 +3268,7 @@ packages: slash: 3.0.0 dev: true - /jest-runner/29.3.1: + /jest-runner@29.3.1: resolution: {integrity: sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3279,7 +3297,7 @@ packages: - supports-color dev: true - /jest-runtime/29.3.1: + /jest-runtime@29.3.1: resolution: {integrity: sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3309,14 +3327,14 @@ packages: - supports-color dev: true - /jest-snapshot/29.3.1: + /jest-snapshot@29.3.1: resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.20.12 '@babel/generator': 7.20.7 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 + '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.20.12) '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 '@jest/expect-utils': 29.3.1 @@ -3324,7 +3342,7 @@ packages: '@jest/types': 29.3.1 '@types/babel__traverse': 7.18.3 '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.20.12) chalk: 4.1.2 expect: 29.3.1 graceful-fs: 4.2.10 @@ -3341,7 +3359,7 @@ packages: - supports-color dev: true - /jest-util/29.3.1: + /jest-util@29.3.1: resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3353,7 +3371,7 @@ packages: picomatch: 2.3.1 dev: true - /jest-validate/29.3.1: + /jest-validate@29.3.1: resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3365,7 +3383,7 @@ packages: pretty-format: 29.3.1 dev: true - /jest-watcher/29.3.1: + /jest-watcher@29.3.1: resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3379,7 +3397,7 @@ packages: string-length: 4.0.2 dev: true - /jest-worker/29.3.1: + /jest-worker@29.3.1: resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3389,7 +3407,7 @@ packages: supports-color: 8.1.1 dev: true - /jest/29.3.1_@types+node@18.11.18: + /jest@29.3.1(@types/node@18.11.18): resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3402,21 +3420,21 @@ packages: '@jest/core': 29.3.1 '@jest/types': 29.3.1 import-local: 3.1.0 - jest-cli: 29.3.1_@types+node@18.11.18 + jest-cli: 29.3.1(@types/node@18.11.18) transitivePeerDependencies: - '@types/node' - supports-color - ts-node dev: true - /js-sdsl/4.3.0: + /js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} dev: true - /js-tokens/4.0.0: + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - /js-yaml/3.14.1: + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true dependencies: @@ -3424,41 +3442,41 @@ packages: esprima: 4.0.1 dev: true - /js-yaml/4.1.0: + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 - /jsesc/0.5.0: + /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true dev: true - /jsesc/2.5.2: + /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true dev: true - /json-parse-even-better-errors/2.3.1: + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - /json-schema-traverse/0.4.1: + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true - /json-stable-stringify-without-jsonify/1.0.1: + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json5/2.2.3: + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true dev: true - /jsonfile/6.1.0: + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: universalify: 2.0.0 @@ -3466,17 +3484,17 @@ packages: graceful-fs: 4.2.10 dev: true - /kleur/3.0.3: + /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} dev: true - /leven/3.1.0: + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} dev: true - /levn/0.4.1: + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} dependencies: @@ -3484,75 +3502,75 @@ packages: type-check: 0.4.0 dev: true - /lines-and-columns/1.2.4: + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - /locate-path/5.0.0: + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} dependencies: p-locate: 4.1.0 dev: true - /locate-path/6.0.0: + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 dev: true - /lodash.debounce/4.0.8: + /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true - /lodash.memoize/4.1.2: + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true - /lodash.merge/4.6.2: + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /lru-cache/5.1.1: + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: yallist: 3.1.1 dev: true - /lru-cache/6.0.0: + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} dependencies: yallist: 4.0.0 dev: true - /make-dir/3.1.0: + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: semver: 6.3.0 dev: true - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /makeerror/1.0.12: + /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 dev: true - /merge-stream/2.0.0: + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true - /merge2/1.4.1: + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} dev: true - /micromatch/4.0.5: + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: @@ -3560,37 +3578,37 @@ packages: picomatch: 2.3.1 dev: true - /mimic-fn/2.1.0: + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} dev: true - /minimatch/3.1.2: + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /mitt/3.0.0: + /mitt@3.0.0: resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==} dev: false - /mkdirp-classic/0.5.3: + /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: false - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - /natural-compare-lite/1.4.0: + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true - /natural-compare/1.4.0: + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /node-fetch/2.6.7: + /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} peerDependencies: @@ -3602,39 +3620,39 @@ packages: whatwg-url: 5.0.0 dev: false - /node-int64/0.4.0: + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true - /node-releases/2.0.8: + /node-releases@2.0.8: resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} dev: true - /normalize-path/3.0.0: + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true - /npm-run-path/4.0.1: + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} dependencies: path-key: 3.1.1 dev: true - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - /onetime/5.1.2: + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 dev: true - /optionator/0.9.1: + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: @@ -3646,46 +3664,46 @@ packages: word-wrap: 1.2.3 dev: true - /p-limit/2.3.0: + /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} dependencies: p-try: 2.2.0 dev: true - /p-limit/3.1.0: + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 dev: true - /p-locate/4.1.0: + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} dependencies: p-limit: 2.3.0 dev: true - /p-locate/5.0.0: + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 dev: true - /p-try/2.2.0: + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} dev: true - /parent-module/1.0.1: + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} dependencies: callsites: 3.1.0 - /parse-json/5.2.0: + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: @@ -3694,66 +3712,66 @@ packages: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /path-exists/4.0.0: + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: true - /path-is-absolute/1.0.1: + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} dev: true - /path-key/3.1.1: + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /path-type/4.0.0: + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - /pend/1.2.0: + /pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} dev: false - /picocolors/1.0.0: + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true - /picomatch/2.3.1: + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true - /pirates/4.0.5: + /pirates@4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} dev: true - /pkg-dir/4.2.0: + /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} dependencies: find-up: 4.1.0 dev: true - /prelude-ls/1.2.1: + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true - /prettier/2.8.3: + /prettier@2.8.3: resolution: {integrity: sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==} engines: {node: '>=10.13.0'} hasBin: true dev: true - /pretty-format/29.3.1: + /pretty-format@29.3.1: resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: @@ -3762,12 +3780,12 @@ packages: react-is: 18.2.0 dev: true - /progress/2.0.3: + /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} dev: false - /prompts/2.4.2: + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} dependencies: @@ -3775,23 +3793,23 @@ packages: sisteransi: 1.0.5 dev: true - /proxy-from-env/1.1.0: + /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: false - /pump/3.0.0: + /pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: end-of-stream: 1.4.4 once: 1.4.0 dev: false - /punycode/2.3.0: + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} dev: true - /puppeteer-core/19.10.0_typescript@5.0.2: + /puppeteer-core@19.10.0(typescript@5.0.2): resolution: {integrity: sha512-F6btgaEJXcpdw/kJszBhe7t4kx1C42oG159romCagX8ttComuk5dqkmAd/AQc/E4cS3onOUsHI5WgjIstuYsFw==} engines: {node: '>=14.14.0'} peerDependencies: @@ -3800,8 +3818,8 @@ packages: typescript: optional: true dependencies: - '@puppeteer/browsers': 0.4.1_typescript@5.0.2 - chromium-bidi: 0.4.6_7yd6ibrwer4fxzjxd6md3toxiy + '@puppeteer/browsers': 0.4.1(typescript@5.0.2) + chromium-bidi: 0.4.6(devtools-protocol@0.0.1107588) cross-fetch: 3.1.5 debug: 4.3.4 devtools-protocol: 0.0.1107588 @@ -3819,16 +3837,16 @@ packages: - utf-8-validate dev: false - /puppeteer/19.10.0_typescript@5.0.2: + /puppeteer@19.10.0(typescript@5.0.2): resolution: {integrity: sha512-u2ftBcAu1cL5x2uKD5FLhHkN60Z1d5T0iMftiUEyRfdwT0poObRvjpc69F0HbtN/7lFeW2v+VN4pgeVVSOZYWg==} requiresBuild: true dependencies: - '@puppeteer/browsers': 0.4.1_typescript@5.0.2 + '@puppeteer/browsers': 0.4.1(typescript@5.0.2) cosmiconfig: 8.1.3 https-proxy-agent: 5.0.1 progress: 2.0.3 proxy-from-env: 1.1.0 - puppeteer-core: 19.10.0_typescript@5.0.2 + puppeteer-core: 19.10.0(typescript@5.0.2) transitivePeerDependencies: - bufferutil - encoding @@ -3837,21 +3855,21 @@ packages: - utf-8-validate dev: false - /queue-microtask/1.2.3: + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /randombytes/2.1.0: + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: safe-buffer: 5.2.1 dev: true - /react-is/18.2.0: + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /readable-stream/3.6.2: + /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} dependencies: @@ -3860,33 +3878,33 @@ packages: util-deprecate: 1.0.2 dev: false - /regenerate-unicode-properties/10.1.0: + /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} engines: {node: '>=4'} dependencies: regenerate: 1.4.2 dev: true - /regenerate/1.4.2: + /regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} dev: true - /regenerator-runtime/0.13.11: + /regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} dev: true - /regenerator-transform/0.15.1: + /regenerator-transform@0.15.1: resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} dependencies: '@babel/runtime': 7.20.13 dev: true - /regexpp/3.2.0: + /regexpp@3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} dev: true - /regexpu-core/5.2.2: + /regexpu-core@5.2.2: resolution: {integrity: sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==} engines: {node: '>=4'} dependencies: @@ -3898,43 +3916,43 @@ packages: unicode-match-property-value-ecmascript: 2.1.0 dev: true - /regjsgen/0.7.1: + /regjsgen@0.7.1: resolution: {integrity: sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==} dev: true - /regjsparser/0.9.1: + /regjsparser@0.9.1: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true dependencies: jsesc: 0.5.0 dev: true - /require-directory/2.1.1: + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - /resolve-cwd/3.0.0: + /resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 dev: true - /resolve-from/4.0.0: + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - /resolve-from/5.0.0: + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} dev: true - /resolve.exports/1.1.1: + /resolve.exports@1.1.1: resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==} engines: {node: '>=10'} dev: true - /resolve/1.22.1: + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: @@ -3943,19 +3961,19 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /reusify/1.0.4: + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rimraf/3.0.2: + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 dev: true - /rollup-plugin-typescript2/0.34.1_xzkd7boxfqp7uxzhulfcfesazm: + /rollup-plugin-typescript2@0.34.1(rollup@3.10.1)(typescript@5.0.2): resolution: {integrity: sha512-P4cHLtGikESmqi1CA+tdMDUv8WbQV48mzPYt77TSTOPJpERyZ9TXdDgjSDix8Fkqce6soYz3+fa4lrC93IEkcw==} peerDependencies: rollup: '>=1.26.3' @@ -3970,7 +3988,7 @@ packages: typescript: 5.0.2 dev: true - /rollup/3.10.1: + /rollup@3.10.1: resolution: {integrity: sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true @@ -3978,21 +3996,21 @@ packages: fsevents: 2.3.2 dev: true - /run-parallel/1.2.0: + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /safe-buffer/5.2.1: + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - /semver/6.3.0: + /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true dev: true - /semver/7.3.8: + /semver@7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} hasBin: true @@ -4000,72 +4018,72 @@ packages: lru-cache: 6.0.0 dev: true - /serialize-javascript/6.0.1: + /serialize-javascript@6.0.1: resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} dependencies: randombytes: 2.1.0 dev: true - /shebang-command/2.0.0: + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 dev: true - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: true - /signal-exit/3.0.7: + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true - /sisteransi/1.0.5: + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true - /slash/3.0.0: + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} dev: true - /smob/0.0.6: + /smob@0.0.6: resolution: {integrity: sha512-V21+XeNni+tTyiST1MHsa84AQhT1aFZipzPpOFAVB8DkHzwJyjjAmt9bgwnuZiZWnIbMo2duE29wybxv/7HWUw==} dev: true - /source-map-support/0.5.13: + /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 dev: true - /source-map-support/0.5.21: + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 dev: true - /source-map/0.6.1: + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} dev: true - /sprintf-js/1.0.3: + /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stack-utils/2.0.6: + /stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 dev: true - /string-length/4.0.2: + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} dependencies: @@ -4073,7 +4091,7 @@ packages: strip-ansi: 6.0.1 dev: true - /string-width/4.2.3: + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} dependencies: @@ -4081,58 +4099,58 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string_decoder/1.3.0: + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 dev: false - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - /strip-bom/4.0.0: + /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} dev: true - /strip-final-newline/2.0.0: + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} dev: true - /strip-json-comments/3.1.1: + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /supports-color/5.5.0: + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - /supports-color/7.2.0: + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - /supports-color/8.1.1: + /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} dependencies: has-flag: 4.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /tar-fs/2.1.1: + /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: chownr: 1.1.4 @@ -4141,7 +4159,7 @@ packages: tar-stream: 2.2.0 dev: false - /tar-stream/2.2.0: + /tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} dependencies: @@ -4152,7 +4170,7 @@ packages: readable-stream: 3.6.2 dev: false - /terser/5.16.1: + /terser@5.16.1: resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} engines: {node: '>=10'} hasBin: true @@ -4163,7 +4181,7 @@ packages: source-map-support: 0.5.21 dev: true - /test-exclude/6.0.0: + /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} dependencies: @@ -4172,35 +4190,35 @@ packages: minimatch: 3.1.2 dev: true - /text-table/0.2.0: + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /through/2.3.8: + /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: false - /tmpl/1.0.5: + /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true - /to-fast-properties/2.0.0: + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} dev: true - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 dev: true - /tr46/0.0.3: + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: false - /ts-jest/29.0.5_oi7nmolcnmyqply4ewqwuryc6q: + /ts-jest@29.0.5(@babel/core@7.20.12)(jest@29.3.1)(typescript@5.0.2): resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4224,7 +4242,7 @@ packages: '@babel/core': 7.20.12 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.3.1_@types+node@18.11.18 + jest: 29.3.1(@types/node@18.11.18) jest-util: 29.3.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -4234,15 +4252,15 @@ packages: yargs-parser: 21.1.1 dev: true - /tslib/1.14.1: + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib/2.4.1: + /tslib@2.4.1: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: true - /tsutils/3.21.0_typescript@5.0.2: + /tsutils@3.21.0(typescript@5.0.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: @@ -4252,46 +4270,46 @@ packages: typescript: 5.0.2 dev: true - /type-check/0.4.0: + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.2.1 dev: true - /type-detect/4.0.8: + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} dev: true - /type-fest/0.20.2: + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: true - /type-fest/0.21.3: + /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} dev: true - /typescript/5.0.2: + /typescript@5.0.2: resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} engines: {node: '>=12.20'} hasBin: true - /unbzip2-stream/1.4.3: + /unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} dependencies: buffer: 5.7.1 through: 2.3.8 dev: false - /unicode-canonical-property-names-ecmascript/2.0.0: + /unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} dev: true - /unicode-match-property-ecmascript/2.0.0: + /unicode-match-property-ecmascript@2.0.0: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} dependencies: @@ -4299,22 +4317,22 @@ packages: unicode-property-aliases-ecmascript: 2.1.0 dev: true - /unicode-match-property-value-ecmascript/2.1.0: + /unicode-match-property-value-ecmascript@2.1.0: resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} engines: {node: '>=4'} dev: true - /unicode-property-aliases-ecmascript/2.1.0: + /unicode-property-aliases-ecmascript@2.1.0: resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} engines: {node: '>=4'} dev: true - /universalify/2.0.0: + /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} dev: true - /update-browserslist-db/1.0.10_browserslist@4.21.4: + /update-browserslist-db@1.0.10(browserslist@4.21.4): resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true peerDependencies: @@ -4325,17 +4343,17 @@ packages: picocolors: 1.0.0 dev: true - /uri-js/4.4.1: + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.0 dev: true - /util-deprecate/1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false - /v8-to-istanbul/9.0.1: + /v8-to-istanbul@9.0.1: resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} engines: {node: '>=10.12.0'} dependencies: @@ -4344,24 +4362,24 @@ packages: convert-source-map: 1.9.0 dev: true - /walker/1.0.8: + /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 dev: true - /webidl-conversions/3.0.1: + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false - /whatwg-url/5.0.0: + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 dev: false - /which/2.0.2: + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true @@ -4369,12 +4387,12 @@ packages: isexe: 2.0.0 dev: true - /word-wrap/1.2.3: + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: true - /wrap-ansi/7.0.0: + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} dependencies: @@ -4382,10 +4400,10 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /write-file-atomic/4.0.2: + /write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: @@ -4393,7 +4411,7 @@ packages: signal-exit: 3.0.7 dev: true - /ws/8.13.0: + /ws@8.13.0: resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'} peerDependencies: @@ -4406,23 +4424,23 @@ packages: optional: true dev: false - /y18n/5.0.8: + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - /yallist/3.1.1: + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true - /yallist/4.0.0: + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yargs-parser/21.1.1: + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - /yargs/17.7.1: + /yargs@17.7.1: resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} engines: {node: '>=12'} dependencies: @@ -4434,14 +4452,14 @@ packages: y18n: 5.0.8 yargs-parser: 21.1.1 - /yauzl/2.10.0: + /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 dev: false - /yocto-queue/0.1.0: + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true diff --git a/publish/README.md b/publish/README.md index 61f5a091..1afd81ce 100644 --- a/publish/README.md +++ b/publish/README.md @@ -9,20 +9,21 @@ x-crawl is a flexible Node.js multipurpose crawler library. The usage is flexibl ## Features - **🔥 Asynchronous Synchronous** - Just change the mode property to toggle asynchronous or synchronous crawling mode. -- **⚙️Multiple purposes** - It can crawl pages, crawl interfaces, crawl files and poll crawls to meet the needs of various scenarios. +- **⚙️ Multiple purposes** - It can crawl pages, crawl interfaces, crawl files and poll crawls to meet the needs of various scenarios. - **🖋️ Flexible writing style** - The same crawling API can be adapted to multiple configurations, and each configuration method is very unique. -- **👀Device Fingerprinting** - Zero configuration or custom configuration, avoid fingerprinting to identify and track us from different locations. - **⏱️ Interval Crawling** - No interval, fixed interval and random interval to generate or avoid high concurrent crawling. -- **🔄 Failed Retry** - Avoid crawling failure due to transient problems, unlimited retries. +- **🔄 Failed Retry** - Avoid crawling failure due to short-term problems, and customize the number of retries. +- **➡️ Proxy Rotation** - Auto-rotate proxies with failure retry, custom error times and HTTP status codes. +- **👀 Device Fingerprinting** - Zero configuration or custom configuration, avoid fingerprinting to identify and track us from different locations. - **🚀 Priority Queue** - According to the priority of a single crawling target, it can be crawled ahead of other targets. - **☁️ Crawl SPA** - Crawl SPA (Single Page Application) to generate pre-rendered content (aka "SSR" (Server Side Rendering)). - **⚒️ Control Page** - You can submit form, keyboard input, event operation, generate screenshots of the page, etc. -- **🧾 Capture Record** - Capture and record the crawled information, and highlight it on the console. +- **🧾 Capture Record** - Capture and record crawling, and use colored strings to remind in the terminal. - **🦾 TypeScript** - Own types, implement complete types through generics. ## Relationship with Puppeteer -The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built in, you only need to pass in some configuration options to complete some operations, and the result will expose Brower instances and Page instances. +The crawlPage API has built-in [puppeteer](https://github.com/puppeteer/puppeteer), you only need to pass in some configuration options to complete some operations, the result will expose the Brower instance and Page instance, you get Brower instance and Page instance will be intact, x-crawl will not rewrite them. # Table of Contents @@ -48,9 +49,10 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [onBeforeSaveItemFile](#onBeforeSaveItemFile) - [Start Polling](#Start-Polling) - [Config Priority](#Config-Priority) - - [Custom Device Fingerprint](#Custom-Device-Fingerprint) - [Interval Time](#Interval-Time) - [Fail Retry](#Fail-Retry) + - [Rotate Proxy](#Rotate-Proxy) + - [Custom Device Fingerprint](#Custom-Device-Fingerprint) - [Priority Queue](#Priority-Queue) - [About Results](#About-Results) - [TypeScript](#TypeScript) @@ -85,21 +87,20 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [Type](#Type-4) - [Example](#Example-5) - [Types](#Types) - - [API Config](#API-config) + - [API Config](#API-Config) - [XCrawlConfig](#XCrawlConfig) - - [Detail target config](#Detail-target-config) + - [Detail Target Config](#Detail-Target-Config) - [CrawlPageDetailTargetConfig](#CrawlPageDetailTargetConfig) - [CrawlDataDetailTargetConfig](#CrawlDataDetailTargetConfig) - [CrawlFileDetailTargetConfig](#CrawlFileDetailTargetConfig) - - [Advanced config](#Advanced-config) + - [Advanced Config](#Advanced-Config) - [CrawlPageAdvancedConfig](#CrawlPageAdvancedConfig) - [CrawlDataAdvancedConfig](#CrawlDataAdvancedConfig) - [CrawlFileAdvancedConfig](#CrawlFileAdvancedConfig) - [StartPollingConfig](#StartPollingConfig) - - [Crawl other config](#Crawl-other-config) + - [Crawl Other Config](#Crawl-Other-Config) - [CrawlCommonConfig](#CrawlCommonConfig) - [DetailTargetFingerprintCommon](#DetailTargetFingerprintCommon) - - [AdvancedFingerprintCommon](#AdvancedFingerprintCommon) - [Mobile](#Mobile) - [Platform](#Platform) - [PageCookies](#PageCookies) @@ -107,13 +108,15 @@ The crawlPage API has [puppeteer](https://github.com/puppeteer/puppeteer) built - [IntervalTime](#IntervalTime) - [API Result](#API-Result) - [XCrawlInstance](#XCrawlInstance) - - [CrawlCommonRes](#CrawlCommonRes) - - [CrawlPageSingleRes](#CrawlPageSingleRes) - - [CrawlDataSingleRes](#CrawlDataSingleRes) - - [CrawlFileSingleRes](#CrawlFileSingleRes) + - [CrawlCommonResult](#CrawlCommonResult) + - [CrawlPageSingleResult](#CrawlPageSingleResult) + - [CrawlDataSingleResult](#CrawlDataSingleResult) + - [CrawlFileSingleResult](#CrawlFileSingleResult) - [API Other](#API-Other) - [AnyObject](#AnyObject) - [More](#More) + - [Community](#Community) + - [Issues](#Issues) ## Install @@ -125,14 +128,14 @@ npm install x-crawl ## Example -Take some pictures of Airbnb hawaii experience and Plus listings automatically every day as an example: +Take the automatic acquisition of some photos of experiences and homes around the world every day as an example: ```js // 1.Import module ES/CJS import xCrawl from 'x-crawl' // 2.Create a crawler instance -const myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } }) +const myXCrawl = xCrawl({maxRetry: 3,intervalTime: { max: 3000, min: 2000 }}) // 3.Set the crawling task /* @@ -141,23 +144,31 @@ const myXCrawl = xCrawl({ maxRetry: 3, intervalTime: { max: 3000, min: 2000 } }) */ myXCrawl.startPolling({ d: 1 }, async (count, stopPolling) => { // Call crawlPage API to crawl Page - const res = await myXCrawl.crawlPage([ - 'https://zh.airbnb.com/s/hawaii/experiences', - 'https://zh.airbnb.com/s/hawaii/plus_homes' - ]) + const res = await myXCrawl.crawlPage({ + targets: [ + 'https://www.airbnb.cn/s/experiences', + 'https://www.airbnb.cn/s/plus_homes' + ], + viewport: { width: 1920, height: 1080 } + }) // Store the image URL to targets const targets = [] - const elSelectorMap = ['.c14whb16', '.a1stauiv'] + const elSelectorMap = ['._fig15y', '._aov0j6'] for (const item of res) { const { id } = item const { page } = item.data - // Gets the URL of the page's wheel image element - const boxHandle = await page.$(elSelectorMap[id - 1]) - const urls = await boxHandle!.$$eval('picture img', (imgEls) => { - return imgEls.map((item) => item.src) - }) + // Wait for the page to load + await new Promise((r) => setTimeout(r, 300)) + + // Gets the URL of the page image + const urls = await page!.$$eval( + `${elSelectorMap[id - 1]} img`, + (imgEls) => { + return imgEls.map((item) => item.src) + } + ) targets.push(...urls) // Close page @@ -304,7 +315,7 @@ myXCrawl.crawlPage('https://www.example.com').then(async (res) => { Lifecycle functions owned by the crawlPage API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete ##### onCrawlItemComplete @@ -340,7 +351,7 @@ myXCrawl.crawlData({ targets }).then((res) => { Life cycle functions owned by crawlData API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete ##### onCrawlItemComplete @@ -374,7 +385,7 @@ myXCrawl Life cycle functions owned by crawlFile API: -- onCrawlItemComplete: Called when each crawl item is completed and processed +- onCrawlItemComplete: Call back when each crawl is complete - onBeforeSaveItemFile: Callback before saving the file @@ -448,7 +459,7 @@ Some common configurations can be set in these three places: - Application instance configuration (global) - Advanced configuration (partial) -- detailed target configuration (separately) +- Detailed target configuration (separately) The priority is: detailed target configuration > advanced configuration > application instance configuration @@ -457,73 +468,45 @@ Take crawlPage to crawl two pages as an example: ```js import xCrawl from 'x-crawl' -// Application instance configuration -const myXCrawl = xCrawl({ - intervalTime: { max: 3000, min: 1000 } -}) - -// advanced configuration -myXCrawl.crawlPage({ - targets: [ - 'https://www.example.com/page-1', - { - // Detailed target configuration - url: 'https://www.example.com/page-1', - viewport: { width: 1920, height: 1080 } - } - ], - intervalTime: 1000, - viewport: { width: 800, height: 600 } +// Application instance configurationconst testXCrawl = xCrawl({ + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } }) -``` - -### Custom Device Fingerprint - -Customize the configuration of device fingerprints to avoid identifying and tracking us from different locations through fingerprint recognition. - -Multiple information can be passed in the fingerprint through advanced usage, and internally it will help you randomly assign each target to targets. It is also possible to set a specific fingerprint for a target directly with the detailed target configuration. - -Take crawlPage as an example: - -```js -import xCrawl from 'x-crawl' - -const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) -myXCrawl +// Advanced configuration +testXCrawl .crawlPage({ targets: [ 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + // Detailed target configuration { - // Specify the fingerprint - url: 'https://www.example.com/page-2', - fingerprint: { - maxWidth: 1980, - minWidth: 1200, - maxHeight: 1080, - minHidth: 800, - platform: 'Android' - } + url: 'https://www.example.com/page-3', + proxy: { urls: ['https://www.example.com/proxy-5'] } } ], - fingerprint: { - // set fingerprint for each target in targets - maxWidth: 1980, - maxHeight: 1080, - userAgents: [ - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36', - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0' + maxRetry: 10, + proxy: { + urls: [ + 'https://www.example.com/proxy-3', + 'https://www.example.com/proxy-4' ], - platforms: ['Chromium OS', 'iOS', 'Linux', 'macOS', 'Windows'] + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] } }) .then((res) => {}) +}) ``` -For more fingerprint options, you can go to the corresponding configuration to view. - -In the above example, the interval time is set in both **Application Instance Configuration** and **Advanced Configuration**, then the interval time of **Advanced Configuration** will prevail. If the viewport is set in **Advanced Configuration** and **Detailed Target Configuration**, then the second target will be based on the viewport of its **Detailed Target Configuration**. +In the above example, **Proxy** is set in **Application Instance Configuration**, **Advanced Configuration** and **Detailed Target Configuration**, page3 will use its own proxy configuration, page1 and page2 will use the proxy configuration of the advanced configuration. ### Interval Time @@ -555,6 +538,8 @@ The intervalTime option defaults to undefined . If there is a setting value, it It can avoid crawling failure due to temporary problems, and will wait for the end of this round of crawling targets to crawl again. +You can create crawler application instance, advanced usage, detailed target these three places Settings. + ```js import xCrawl from 'x-crawl' @@ -567,6 +552,169 @@ myXCrawl The maxRetry attribute determines how many times to retry. +### Rotate Proxy + +With failed retries, custom error times and HTTP status codes, the proxy is automatically rotated for crawling targets. + +You can create crawler application instance, advanced usage, detailed target these three places Settings. + +Take crawlPage as an example: + +```js +import xCrawl from 'x-crawl' + +const testXCrawl = xCrawl() + +testXCrawl + .crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + 'https://www.example.com/page-4', + // Undelegate for this target + { url: 'https://www.example.com/page-6', proxy: null }, + // Set the proxy individually for this target + { + url: 'https://www.example.com/page-6', + proxy: { + urls: [ + 'https://www.example.com/proxy-4', + 'https://www.example.com/proxy-5' + ], + switchByErrorCount: 3 + } + } + ], + maxRetry: 10, + // Set the proxy uniformly for this target + proxy: { + urls: [ + 'https://www.example.com/proxy-1', + 'https://www.example.com/proxy-2', + 'https://www.example.com/proxy-3' + ], + switchByErrorCount: 3, + switchByHttpStatus: [401, 403] + } + }) + .then((res) => {}) +``` + +**Note:** This function needs to cooperate with failure retry to work normally. + +### Custom Device Fingerprint + +Customize the configuration of device fingerprints to avoid identifying and tracking us from different locations through fingerprint recognition. + +Multiple information can be passed in fingerprints through advanced usage, and internally it will help you randomly assign each target to targets. It is also possible to set a specific fingerprint for a target directly with the detailed target configuration. + +Take crawlPage as an example: + +```js +import xCrawl from 'x-crawl' + +const myXCrawl = xCrawl({ intervalTime: { max: 5000, min: 3000 } }) + +myXCrawl.crawlPage({ + targets: [ + 'https://www.example.com/page-1', + 'https://www.example.com/page-2', + 'https://www.example.com/page-3', + // Cancel the fingerprint for this target + { url: 'https://www.example.com/page-4', fingerprint: null }, + // Set a separate fingerprint for this target + { + url: 'https://www.example.com/page-5', + fingerprint: { + mobile: 'random', + platform: 'Windows', + acceptLanguage: `zh-CN,zh;q=0.9,en;q=0.8`, + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { name: 'Chrome', maxMinorVersion: 10, maxPatchVersion: 5615 }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 } + ] + } + } + } + ], + // Set fingerprints uniformly for this target + fingerprints: [ + // Device fingerprint 1 + { + maxWidth: 1024, + maxHeight: 800, + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + // Browser version + maxMajorVersion: 112, + minMajorVersion: 100, + maxMinorVersion: 20, + maxPatchVersion: 5000 + }, + { + name: 'Safari', + maxMajorVersion: 537, + minMajorVersion: 500, + maxMinorVersion: 36, + maxPatchVersion: 5000 + } + ] + } + }, + // Device fingerprint 2 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 91, + minMajorVersion: 88, + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 }, + { name: 'Edg', maxMinorVersion: 10, maxPatchVersion: 864 } + ] + } + }, + // Device fingerprint 3 + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', + versions: [ + { + name: 'Firefox', + maxMajorVersion: 47, + minMajorVersion: 43, + maxMinorVersion: 10, + maxPatchVersion: 5000 + } + ] + } + } + ] +}) +``` + +For more fingerprint options, you can go to the corresponding configuration to view. + ### Priority Queue A priority queue allows a crawl target to be sent first. @@ -595,6 +743,7 @@ Each crawl target will generate a detail object, which will contain the followin - isSuccess: Whether to crawl successfully - maxRetry: The maximum number of retries for this crawling target - retryCount: The number of times the crawling target has been retried +- proxyDetails: record the proxy situation - crawlErrorQueue: Error collection of the crawl target - data: the crawling data of the crawling target @@ -657,23 +806,23 @@ The crawlPage API is a function. A type is an [overloaded function](https://www. type crawlPage = { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } ``` @@ -684,7 +833,7 @@ type crawlPage = { **Return value type:** -- Look at the [CrawlPageSingleRes](#CrawlPageSingleRes) type +- Look at the [CrawlPageSingleResult](#CrawlPageSingleResult) type #### Example @@ -808,23 +957,23 @@ The crawlData API is a function. A type is an [overloaded function](https://www. type crawlData = { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } ``` @@ -835,7 +984,7 @@ type crawlData = { **Return value type:** -- Look at the [CrawlDataSingleRes](#CrawlDataSingleRes) type +- Look at the [CrawlDataSingleResult](#CrawlDataSingleResult) type #### Example @@ -965,18 +1114,18 @@ The crawlFile API is a function. A type is an [overloaded function](https://www. type crawlFile = { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } ``` @@ -987,7 +1136,7 @@ type crawlFile = { **Return value type:** -- Look at the [CrawlFileSingleRes](#CrawlFileSingleRes) type +- Look at the [CrawlFileSingleResult](#CrawlFileSingleResult) type #### Example @@ -1128,7 +1277,7 @@ myXCrawl.startPolling({ h: 2, m: 30 }, (count, stopPolling) => { ## Types -### API config +### API Config #### XCrawlConfig @@ -1165,9 +1314,9 @@ export interface CrawlPageDetailTargetConfig extends CrawlCommonConfig { viewport?: Viewport | null // puppeteer fingerprint?: | (DetailTargetFingerprintCommon & { - maxWidth: number + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number }) | null @@ -1231,7 +1380,7 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { - extension: string - fingerprint: undefined -#### Advanced config +#### Advanced Config ##### CrawlPageAdvancedConfig @@ -1239,27 +1388,26 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlPageDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon & { - maxWidth: number + fingerprints?: (DetailTargetFingerprintCommon & { + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number - } + })[] headers?: AnyObject cookies?: PageCookies - viewport?: Viewport // puppeteer + viewport?: Viewport - onCrawlItemComplete?: (crawlPageSingleRes: CrawlPageSingleRes) => void + onCrawlItemComplete?: (crawlPageSingleResult: CrawlPageSingleResult) => void } ``` **Default Value** - targets: undefined - - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - cookies: undefined - viewport: undefined @@ -1271,11 +1419,13 @@ export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlDataDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject - onCrawlItemComplete?: (crawlDataSingleRes: CrawlDataSingleRes) => void + onCrawlItemComplete?: ( + crawlDataSingleResult: CrawlDataSingleResult + ) => void } ``` @@ -1283,7 +1433,7 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - onCrawlItemComplete: undefined @@ -1293,13 +1443,13 @@ export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlFileDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject storeDir?: string extension?: string - onCrawlItemComplete?: (crawlFileSingleRes: CrawlFileSingleRes) => void + onCrawlItemComplete?: (crawlFileSingleResult: CrawlFileSingleResult) => void onBeforeSaveItemFile?: (info: { id: number fileName: string @@ -1313,7 +1463,7 @@ export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { - targets: undefined - intervalTime: undefined -- fingerprint: undefined +- fingerprints: undefined - headers: undefined - storeDir: \_\_dirname - extension: string @@ -1336,15 +1486,19 @@ export interface StartPollingConfig { - h: undefined - m: undefined -#### Crawl other config +#### Crawl Other Config ##### CrawlCommonConfig ```ts export interface CrawlCommonConfig { - timeout?: number - proxy?: string - maxRetry?: number + timeout?: number | null + proxy?: { + urls: string[] + switchByHttpStatus?: number[] + switchByErrorCount?: number + } | null + maxRetry?: number | null } ``` @@ -1358,45 +1512,34 @@ export interface CrawlCommonConfig { ```ts export interface DetailTargetFingerprintCommon { - userAgent?: string ua?: string + mobile?: '?0' | '?1' | 'random' platform?: Platform platformVersion?: string - mobile?: Mobile acceptLanguage?: string + userAgent?: { + value: string + versions?: { + name: string + maxMajorVersion?: number + minMajorVersion?: number + maxMinorVersion?: number + minMinorVersion?: number + maxPatchVersion?: number + minPatchVersion?: number + }[] + } } ``` **Default Value** -- userAgent: undefined - ua: undefined +- mobile: undefined - platform: undefined - platformVersion: undefined -- mobile: undefined - acceptLanguage: undefined - -##### AdvancedFingerprintCommon - -```ts -export interface AdvancedFingerprintCommon { - userAgents?: string[] - uas?: string[] - platforms?: Platform[] - platformVersions?: string[] - mobiles?: Mobile[] - acceptLanguages?: string[] -} -``` - -**Default Value** - -- userAgents: undefined -- uas: undefined -- platforms: undefined -- platformVersions: undefined -- mobiles: undefined -- acceptLanguages: undefined +- userAgent: undefined ##### Mobile @@ -1423,8 +1566,8 @@ export type Platform = ```ts export type PageCookies = | string - | Protocol.Network.CookieParam - | Protocol.Network.CookieParam[] + | Protocol.Network.CookieParam // puppeteer + | Protocol.Network.CookieParam[] // puppeteer ``` ##### Method @@ -1459,7 +1602,7 @@ export type Method = export type IntervalTime = number | { max: number; min?: number } ``` -### API result +### API Result #### XCrawlInstance @@ -1468,62 +1611,62 @@ export interface XCrawlInstance { crawlPage: { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (res: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (res: CrawlPageSingleResult[]) => void + ): Promise } crawlData: { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (res: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (res: CrawlDataSingleResult[]) => void + ): Promise[]> } crawlFile: { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (res: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (res: CrawlFileSingleResult[]) => void + ): Promise } startPolling: ( @@ -1533,28 +1676,30 @@ export interface XCrawlInstance { } ``` -#### CrawlCommonRes +#### CrawlCommonResult ```ts -export interface CrawlCommonRes { +export interface CrawlCommonResult { id: number isSuccess: boolean maxRetry: number retryCount: number + proxyDetails: ProxyDetails crawlErrorQueue: Error[] } ``` -- id: Generated according to the order of crawling targets, if there is a priority, it will be generated according to the priority -- isSuccess: Whether to crawl successfully -- maxRetry: The maximum number of retries for this crawling target -- retryCount: The number of times the crawling target has been retried -- crawlErrorQueue: Error collection of the crawl target +- id:Generated according to the order in which the target is climbed, or by priority, if any +- isSuccess:Whether the climb is successful +- maxRetry:Maximum number of retries of the crawl target +- retryCount:Maximum number of retries of the crawl target +- proxyDetails:Record agent status +- crawlErrorQueue:Error collection for the crawl target -#### CrawlPageSingleRes +#### CrawlPageSingleResult ```ts -export interface CrawlPageSingleRes extends CrawlCommonRes { +export interface CrawlPageSingleResult extends CrawlCommonResult { data: { browser: Browser // puppeteer response: HTTPResponse | null // puppeteer @@ -1563,25 +1708,25 @@ export interface CrawlPageSingleRes extends CrawlCommonRes { } ``` -#### CrawlDataSingleRes +#### CrawlDataSingleResult ```ts -export interface CrawlDataSingleRes extends CrawlCommonRes { +export interface CrawlDataSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined - headers: IncomingHttpHeaders // node http + headers: IncomingHttpHeaders // nodejs http data: D } | null } ``` -#### CrawlFileSingleRes +#### CrawlFileSingleResult ```ts -export interface CrawlFileSingleRes extends CrawlCommonRes { +export interface CrawlFileSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined - headers: IncomingHttpHeaders // node http + headers: IncomingHttpHeaders // nodejs http data: { isSuccess: boolean fileName: string @@ -1606,6 +1751,10 @@ export interface AnyObject extends Object { ## More -If you have **problems, needs, good suggestions** please raise **Issues** in https://github.com/coder-hxl/x-crawl/issues. +### Community + +**GitHub Discussions:** May be discussed through [GitHub Discussions](https://github.com/coder-hxl/x-crawl/discussions). + +### Issues -Thank you all for your support. +If you have questions, needs, or good suggestions, you can raise them at [GitHub Issues](https://github.com/coder-hxl/x-crawl/issues). diff --git a/publish/package.json b/publish/package.json index b050f2b9..3e559856 100644 --- a/publish/package.json +++ b/publish/package.json @@ -1,6 +1,6 @@ { "name": "x-crawl", - "version": "6.0.1", + "version": "7.0.0", "author": "coderHXL", "description": "x-crawl is a flexible Node.js multifunctional crawler library.", "license": "MIT", diff --git a/script/server.mjs b/script/server.mjs new file mode 100644 index 00000000..b8b59e41 --- /dev/null +++ b/script/server.mjs @@ -0,0 +1,12 @@ +import tsPlugin from 'rollup-plugin-typescript2' +import terserPlugin from '@rollup/plugin-terser' +import runPlugin from '@rollup/plugin-run' + +export default { + input: 'test/server/index.ts', + output: { + file: 'test/server/index.js', + format: 'cjs' + }, + plugins: [tsPlugin(), terserPlugin(), runPlugin({ stdin: { clear: true } })] +} diff --git a/src/api.ts b/src/api.ts index d0f205e6..a53c1b28 100644 --- a/src/api.ts +++ b/src/api.ts @@ -3,7 +3,7 @@ import { writeFile } from 'node:fs/promises' import path from 'node:path' import puppeteer, { Browser, HTTPResponse, Page, Protocol } from 'puppeteer' -import { DetailInfo, controller } from './controller' +import { Device, controller, isCrawlStatusInHttpStatus } from './controller' import { Request, request } from './request' import { quickSort } from './sort' import { @@ -12,9 +12,10 @@ import { isUndefined, log, logError, + logStart, + logStatistics, logSuccess, logWarn, - mkdirDirSync, random } from './utils' @@ -23,58 +24,38 @@ import { CrawlFileDetailTargetConfig, CrawlPageDetailTargetConfig, PageCookies, - CrawlPageSingleRes, + CrawlPageSingleResult, StartPollingConfig, CrawlPageAdvancedConfig, - CrawlDataSingleRes, - CrawlFileSingleRes, + CrawlDataSingleResult, + CrawlFileSingleResult, CrawlFileAdvancedConfig, CrawlDataAdvancedConfig, IntervalTime, - DetailTargetFingerprintCommon, - Platform, - Mobile + DetailTargetFingerprintCommon } from './types/api' import { LoaderXCrawlConfig } from './types' -import { AnyObject } from './types/common' -import { randomFingerprint } from './default' +import { fingerprints } from './default' /* Types */ -// Loader -type LoaderHasConfig = { - timeout: number - maxRetry: number - priority: number -} - -export type LoaderCrawlPageDetail = CrawlPageDetailTargetConfig & - LoaderHasConfig - -export type LoaderCrawlDataDetail = CrawlDataDetailTargetConfig & - LoaderHasConfig - -export type LoaderCrawlFileDetail = CrawlFileDetailTargetConfig & - LoaderHasConfig - // Extra config export interface ExtraCommonConfig { + type: 'page' | 'data' | 'file' + intervalTime: IntervalTime | undefined } interface ExtraPageConfig extends ExtraCommonConfig { - // 存放报错的 Page - errorPageMap: Map - browser: Browser onCrawlItemComplete: - | ((crawlPageSingleRes: CrawlPageSingleRes) => void) + | ((crawlPageSingleResult: CrawlPageSingleResult) => void) | undefined } interface ExtraDataConfig extends ExtraCommonConfig { onCrawlItemComplete: - | ((crawlDataSingleRes: CrawlDataSingleRes) => void) + | ((crawlDataSingleResult: CrawlDataSingleResult) => void) | undefined } @@ -82,7 +63,7 @@ interface ExtraFileConfig extends ExtraCommonConfig { saveFileErrorArr: { message: string; valueOf: () => number }[] saveFilePendingQueue: Promise[] onCrawlItemComplete: - | ((crawlFileSingleRes: CrawlFileSingleRes) => void) + | ((crawlFileSingleResult: CrawlFileSingleResult) => void) | undefined onBeforeSaveItemFile: | ((info: { @@ -101,25 +82,75 @@ interface PageSingleCrawlResult { } // Create config -interface CrawlPageConfigOriginal { +// Loader +export type ProxyDetails = { url: string; state: boolean }[] + +type LoaderCommonConfig = { + proxyUrl?: string + proxyDetails: ProxyDetails +} + +type LoaderHasConfig = { + timeout?: number + maxRetry: number + priority: number +} + +export type LoaderCrawlPageDetail = LoaderCommonConfig & + LoaderHasConfig & + CrawlPageDetailTargetConfig + +export type LoaderCrawlDataDetail = LoaderCommonConfig & + LoaderHasConfig & + CrawlDataDetailTargetConfig + +export type LoaderCrawlFileDetail = LoaderCommonConfig & + LoaderHasConfig & + CrawlFileDetailTargetConfig + +// AdvancedDetailTargets +interface CrawlPageAdvancedDetailTargetsConfig extends CrawlPageAdvancedConfig { detailTargets: CrawlPageDetailTargetConfig[] +} + +interface CrawlDataAdvancedDetailTargetsConfig + extends CrawlDataAdvancedConfig { + detailTargets: CrawlDataDetailTargetConfig[] +} + +interface CrawlFileAdvancedDetailTargetsConfig extends CrawlFileAdvancedConfig { + detailTargets: CrawlFileDetailTargetConfig[] +} + +// CrawlConfig +interface CrawlPageConfig { + detailTargets: LoaderCrawlPageDetail[] intervalTime: IntervalTime | undefined + + selectFingerprintIndexs: number[] + onCrawlItemComplete: - | ((crawlPageSingleRes: CrawlPageSingleRes) => void) + | ((crawlPageSingleResult: CrawlPageSingleResult) => void) | undefined } -interface CrawlDataConfigOriginal { - detailTargets: CrawlDataDetailTargetConfig[] +interface CrawlDataConfig { + detailTargets: LoaderCrawlDataDetail[] intervalTime: IntervalTime | undefined + + selectFingerprintIndexs: number[] + onCrawlItemComplete: - | ((crawlDataSingleRes: CrawlDataSingleRes) => void) + | ((crawlDataSingleResult: CrawlDataSingleResult) => void) | undefined } -interface CrawlFileConfigOriginal { - detailTargets: CrawlFileDetailTargetConfig[] +interface CrawlFileConfig { + detailTargets: LoaderCrawlFileDetail[] intervalTime: IntervalTime | undefined + + selectFingerprintIndexs: number[] + onBeforeSaveItemFile: | ((info: { id: number @@ -129,22 +160,10 @@ interface CrawlFileConfigOriginal { }) => Promise) | undefined onCrawlItemComplete: - | ((crawlDataSingleRes: CrawlDataSingleRes) => void) + | ((crawlDataSingleResult: CrawlDataSingleResult) => void) | undefined } -type CrawlPageConfig = Omit & { - detailTargets: LoaderCrawlPageDetail[] -} - -type CrawlDataConfig = Omit & { - detailTargets: LoaderCrawlDataDetail[] -} - -type CrawlFileConfig = Omit & { - detailTargets: LoaderCrawlFileDetail[] -} - // API unite config type UniteCrawlPageConfig = | string @@ -225,7 +244,7 @@ function loaderCommonFingerprintToDetailTarget( | CrawlFileDetailTargetConfig, fingerprint: DetailTargetFingerprintCommon ) { - const { userAgent, ua, platform, platformVersion, mobile, acceptLanguage } = + const { ua, platform, platformVersion, mobile, acceptLanguage, userAgent } = fingerprint let headers = detail.headers @@ -234,16 +253,17 @@ function loaderCommonFingerprintToDetailTarget( detail.headers = headers = {} } - // 1.user-agent - if (userAgent) { - headers['user-agent'] = userAgent - } - - // 2.sec-ch-ua + // 1.sec-ch-ua if (ua) { headers['sec-ch-ua'] = ua } + // 2.sec-ch-ua-mobile + if (mobile) { + headers['sec-ch-ua-mobile'] = + mobile === 'random' ? (random(2) ? '?1' : '?0') : mobile + } + // 3.sec-ch-platform if (platform) { headers['sec-ch-platform'] = platform @@ -254,191 +274,213 @@ function loaderCommonFingerprintToDetailTarget( headers['sec-ch-ua-platform-version'] = platformVersion } - // 5.sec-ch-mobile - if (mobile) { - headers['sec-ch-mobile'] = mobile - } - - // 6.accept-language + // 5.accept-language if (acceptLanguage) { headers['accept-language'] = acceptLanguage } + + // 6.user-agent + if (userAgent) { + let value = userAgent.value + + userAgent.versions?.forEach((version) => { + const { + name, + maxMajorVersion, + minMajorVersion, + maxMinorVersion, + minMinorVersion, + maxPatchVersion, + minPatchVersion + } = version + + const nameSplit = value.split(`${name}/`) + const versionSplit: any[] = nameSplit[1].split(' ')[0].split('.') + const originalVersion = versionSplit.join('.') + + if (!isUndefined(maxMajorVersion)) { + versionSplit[0] = + maxMajorVersion === minMajorVersion + ? maxMajorVersion + : random(maxMajorVersion, minMajorVersion) + } + + if (!isUndefined(maxMinorVersion)) { + versionSplit[1] = + maxMinorVersion === minMinorVersion + ? maxMinorVersion + : random(maxMinorVersion, minMinorVersion) + } + + if (!isUndefined(maxPatchVersion)) { + versionSplit[2] = + maxPatchVersion === minPatchVersion + ? maxPatchVersion + : random(maxPatchVersion, minPatchVersion) + } + + const searchValue = `${name}/${originalVersion}` + const replaceValue = `${name}/${versionSplit.join('.')}` + value = value.replace(searchValue, replaceValue) + }) + + headers['user-agent'] = value + } } function loaderPageFingerprintToDetailTarget( detail: CrawlPageDetailTargetConfig, fingerprint: { - maxWidth: number + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number } ) { const { maxWidth, minWidth, maxHeight, minHidth } = fingerprint + const viewport: any = detail.viewport ?? {} // 1.width / height - const width = maxWidth === minWidth ? maxWidth : random(maxWidth, minWidth) - const height = - maxHeight === minHidth ? maxHeight : random(maxHeight, minHidth) - const viewport = detail.viewport - if (!viewport) { - detail.viewport = { width, height } - } else { - viewport.width = width - viewport.height = height + if (maxWidth) { + viewport.width = + maxWidth === minWidth ? maxWidth : random(maxWidth, minWidth) + } + + if (maxHeight) { + viewport.height = + maxHeight === minHidth ? maxHeight : random(maxHeight, minHidth) + } + + if (Object.hasOwn(viewport, 'width') && Object.hasOwn(viewport, 'height')) { + detail.viewport = viewport } } function loaderCommonConfigToCrawlConfig( xCrawlConfig: LoaderXCrawlConfig, - advancedConfig: - | CrawlPageAdvancedConfig - | CrawlDataAdvancedConfig - | CrawlFileAdvancedConfig, - crawlConfig: - | CrawlPageConfigOriginal - | CrawlDataConfigOriginal - | CrawlFileConfigOriginal + advancedDetailTargetsConfig: + | CrawlPageAdvancedDetailTargetsConfig + | CrawlDataAdvancedDetailTargetsConfig + | CrawlFileAdvancedDetailTargetsConfig, + crawlConfig: CrawlPageConfig | CrawlDataConfig | CrawlFileConfig ) { // 1.detailTargets - crawlConfig.detailTargets.forEach((detail) => { - // detail > advanced > app - const { url, timeout, proxy, maxRetry, priority, headers, fingerprint } = - detail - - // 1.1.baseUrl - if (!isUndefined(xCrawlConfig.baseUrl)) { - detail.url = xCrawlConfig.baseUrl + url - } + crawlConfig.detailTargets = advancedDetailTargetsConfig.detailTargets.map( + (rawDetail) => { + // detail > advanced > app - // 1.2.timeout - if (isUndefined(timeout)) { - if (!isUndefined(advancedConfig.timeout)) { - detail.timeout = advancedConfig.timeout - } else { - detail.timeout = xCrawlConfig.timeout - } - } + const detail = rawDetail as + | LoaderCrawlPageDetail + | LoaderCrawlDataDetail + | LoaderCrawlFileDetail - // 1.3.porxy - if (isUndefined(proxy)) { - if (!isUndefined(advancedConfig.proxy)) { - detail.proxy = advancedConfig.proxy - } else if (!isUndefined(xCrawlConfig.proxy)) { - detail.proxy = xCrawlConfig.proxy - } - } + const { url, timeout, proxy, maxRetry, priority, headers, fingerprint } = + detail - // 1.4.maxRetry - if (isUndefined(maxRetry)) { - if (!isUndefined(advancedConfig.maxRetry)) { - detail.maxRetry = advancedConfig.maxRetry - } else { - detail.maxRetry = xCrawlConfig.maxRetry + // 1.1.baseUrl + if (xCrawlConfig.baseUrl) { + detail.url = xCrawlConfig.baseUrl + url } - } - // 1.5.priority - if (isUndefined(priority)) { - detail.priority = 0 - } - - // 1.6.header - if (isUndefined(headers) && advancedConfig.headers) { - detail.headers = { ...advancedConfig.headers } - } - - // 1.7.fingerprint(公共部分) - if (fingerprint) { - // detaileTarget + // 1.2.timeout + if (isUndefined(timeout)) { + if (!isUndefined(advancedDetailTargetsConfig.timeout)) { + detail.timeout = advancedDetailTargetsConfig.timeout ?? undefined + } else { + detail.timeout = xCrawlConfig.timeout + } + } - loaderCommonFingerprintToDetailTarget(detail, fingerprint) - } else if (isUndefined(fingerprint) && advancedConfig.fingerprint) { - // advancedConfig + // 1.3.maxRetry + if (isUndefined(maxRetry)) { + if (!isUndefined(advancedDetailTargetsConfig.maxRetry)) { + detail.maxRetry = advancedDetailTargetsConfig.maxRetry ?? 0 + } else { + detail.maxRetry = xCrawlConfig.maxRetry + } + } - const { - userAgents, - uas, - platforms, - platformVersions, - mobiles, - acceptLanguages - } = advancedConfig.fingerprint - - // 1.user-agent - const userAgent = userAgents - ? userAgents[random(userAgents.length)] - : undefined - - // 2.sec-ch-ua - const ua = uas ? uas[random(uas.length)] : undefined - - // 3.sec-ch-platform - const platform = platforms - ? platforms[random(platforms.length)] - : undefined - - // 4.sec-ch-platform-version - const platformVersion = platformVersions - ? platformVersions[random(platformVersions.length)] - : undefined - - // 5.sec-ch-mobile - const mobile = mobiles ? mobiles[random(mobiles.length)] : undefined - - // 6.accept-language - const acceptLanguage = acceptLanguages - ? acceptLanguages[random(acceptLanguages.length)] - : undefined - - loaderCommonFingerprintToDetailTarget(detail, { - userAgent, - ua, - platform, - platformVersion, - mobile, - acceptLanguage - }) - } else if (xCrawlConfig.enableRandomFingerprint) { - // xCrawlConfig + // 1.4.proxy + if (isUndefined(proxy)) { + if (!isUndefined(advancedDetailTargetsConfig.proxy)) { + detail.proxy = advancedDetailTargetsConfig.proxy + } else if (!isUndefined(xCrawlConfig.proxy)) { + detail.proxy = xCrawlConfig.proxy + } + } - const { platforms, mobiles } = randomFingerprint + // 1.5.proxyUrl & proxyDetail + if (!isUndefined(detail.proxy?.urls)) { + const urls = detail.proxy!.urls + detail.proxyUrl = urls[0] + detail.proxyDetails = urls.map((url) => ({ url, state: true })) + } else { + // 默认值 + detail.proxyDetails = [] + } - // 1.user-agent - const userAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.${random( - 10 - )}.${random(10000)}.${random(1000)} Safari/537.36` + // 1.6.priority + if (isUndefined(priority)) { + detail.priority = 0 + } - // 2.sec-ch-platform - const platform = platforms[random(platforms.length)] as Platform + // 1.7.header + if (isUndefined(headers) && advancedDetailTargetsConfig.headers) { + detail.headers = { ...advancedDetailTargetsConfig.headers } + } - // 3.sec-ch-mobile - const mobile = mobiles[random(mobiles.length)] as Mobile + // 1.8.fingerprint(公共部分) + if (fingerprint) { + // detaileTarget + + loaderCommonFingerprintToDetailTarget(detail, fingerprint) + } else if ( + isUndefined(fingerprint) && + isArray(advancedDetailTargetsConfig.fingerprints) && + advancedDetailTargetsConfig.fingerprints.length + ) { + // advancedConfig + + const fingerprints = advancedDetailTargetsConfig.fingerprints + const selectFingerprintIndex = random(fingerprints.length) + const fingerprint = fingerprints[selectFingerprintIndex] + + // 记录每个目标选中的指纹索引 + crawlConfig.selectFingerprintIndexs.push(selectFingerprintIndex) + + loaderCommonFingerprintToDetailTarget(detail, fingerprint) + } else if ( + isUndefined(fingerprint) && + !isArray(advancedDetailTargetsConfig.fingerprints) && + xCrawlConfig.enableRandomFingerprint + ) { + // xCrawlConfig + const fingerprint = fingerprints[random(fingerprints.length)] + + loaderCommonFingerprintToDetailTarget(detail, fingerprint) + } - loaderCommonFingerprintToDetailTarget(detail, { - userAgent, - platform, - mobile - }) + return detail } - }) + ) // 2.intervalTime - crawlConfig.intervalTime = advancedConfig.intervalTime + crawlConfig.intervalTime = advancedDetailTargetsConfig.intervalTime if ( - isUndefined(advancedConfig.intervalTime) && + isUndefined(advancedDetailTargetsConfig.intervalTime) && !isUndefined(xCrawlConfig.intervalTime) ) { crawlConfig.intervalTime = xCrawlConfig.intervalTime } // 3.onCrawlItemComplete - crawlConfig.onCrawlItemComplete = advancedConfig.onCrawlItemComplete + crawlConfig.onCrawlItemComplete = + advancedDetailTargetsConfig.onCrawlItemComplete } /* Create config */ /* - 每个创建配置函数的返回值都是类似于进阶版配置 + 每个创建配置函数的返回值都是类似于进阶配置 不同点: - detailTargets 里面将存放的是详细版目标配置 - 不会保留与详细版目标配置相同的选项 @@ -450,96 +492,122 @@ function createCrawlPageConfig( xCrawlConfig: LoaderXCrawlConfig, originalConfig: UniteCrawlPageConfig ): CrawlPageConfig { - const crawlPageConfig: CrawlPageConfigOriginal = { + const crawlPageConfig: CrawlPageConfig = { detailTargets: [], intervalTime: undefined, + + selectFingerprintIndexs: [], + onCrawlItemComplete: undefined } - let advancedConfig: CrawlPageAdvancedConfig = { targets: [] } + let advancedDetailTargetsConfig: CrawlPageAdvancedDetailTargetsConfig = { + targets: [], + detailTargets: [] + } if (isObject(originalConfig) && Object.hasOwn(originalConfig, 'targets')) { // CrawlPageAdvancedConfig 处理 const { targets } = originalConfig as CrawlPageAdvancedConfig - advancedConfig = originalConfig as CrawlPageAdvancedConfig - crawlPageConfig.detailTargets.push( - ...transformTargetToDetailTargets(targets) - ) + advancedDetailTargetsConfig = + originalConfig as CrawlPageAdvancedDetailTargetsConfig + advancedDetailTargetsConfig.detailTargets = + transformTargetToDetailTargets(targets) } else { // string | CrawlPageDetailTargetConfig | (string | CrawlPageDetailTargetConfig)[] 处理 - const detaileTargets = transformTargetToDetailTargets( + advancedDetailTargetsConfig.detailTargets = transformTargetToDetailTargets( originalConfig as | string | CrawlPageDetailTargetConfig | (string | CrawlPageDetailTargetConfig)[] ) - - crawlPageConfig.detailTargets.push(...detaileTargets) } // 装载公共配置 - loaderCommonConfigToCrawlConfig(xCrawlConfig, advancedConfig, crawlPageConfig) + loaderCommonConfigToCrawlConfig( + xCrawlConfig, + advancedDetailTargetsConfig, + crawlPageConfig + ) // 装载单独配置 - crawlPageConfig.detailTargets.forEach((detail) => { + crawlPageConfig.detailTargets.forEach((detail, index) => { // detail > advanced > xCrawl const { cookies, viewport, fingerprint } = detail // 1.cookies - if (isUndefined(cookies) && advancedConfig.cookies) { - detail.cookies = advancedConfig.cookies + if (isUndefined(cookies) && advancedDetailTargetsConfig.cookies) { + detail.cookies = advancedDetailTargetsConfig.cookies } // 2.viewport - if (isUndefined(viewport) && advancedConfig.viewport) { - detail.viewport = advancedConfig.viewport + if (isUndefined(viewport) && advancedDetailTargetsConfig.viewport) { + detail.viewport = advancedDetailTargetsConfig.viewport } // 3.fingerprint if (fingerprint) { loaderPageFingerprintToDetailTarget(detail, fingerprint) - } else if (isUndefined(fingerprint) && advancedConfig.fingerprint) { - loaderPageFingerprintToDetailTarget(detail, advancedConfig.fingerprint) + } else if ( + isUndefined(fingerprint) && + advancedDetailTargetsConfig.fingerprints?.length + ) { + // 从对应的选中记录中取出指纹索引 + const selectFingerprintIndex = + crawlPageConfig.selectFingerprintIndexs[index] + const fingerprint = + advancedDetailTargetsConfig.fingerprints[selectFingerprintIndex] + + loaderPageFingerprintToDetailTarget(detail, fingerprint) } }) - return crawlPageConfig as CrawlPageConfig + return crawlPageConfig } function createCrawlDataConfig( xCrawlConfig: LoaderXCrawlConfig, originalConfig: UniteCrawlDataConfig ): CrawlDataConfig { - const crawlDataConfig: CrawlDataConfigOriginal = { + const crawlDataConfig: CrawlDataConfig = { detailTargets: [], intervalTime: undefined, + + selectFingerprintIndexs: [], + onCrawlItemComplete: undefined } - let advancedConfig: CrawlDataAdvancedConfig = { targets: [] } + let advancedDetailTargetsConfig: CrawlDataAdvancedDetailTargetsConfig = { + targets: [], + detailTargets: [] + } if (isObject(originalConfig) && Object.hasOwn(originalConfig, 'targets')) { // CrawlDataAdvancedConfig 处理 const { targets } = originalConfig as CrawlDataAdvancedConfig - advancedConfig = originalConfig as CrawlDataAdvancedConfig - crawlDataConfig.detailTargets.push( - ...transformTargetToDetailTargets(targets) - ) + advancedDetailTargetsConfig = + originalConfig as CrawlDataAdvancedDetailTargetsConfig + + advancedDetailTargetsConfig.detailTargets = + transformTargetToDetailTargets(targets) } else { // string | CrawlDataDetailTargetConfig | (string | CrawlDataDetailTargetConfig)[] 处理 - const detaileTargets = transformTargetToDetailTargets( + advancedDetailTargetsConfig.detailTargets = transformTargetToDetailTargets( originalConfig as | string | CrawlDataDetailTargetConfig | (string | CrawlDataDetailTargetConfig)[] ) - - crawlDataConfig.detailTargets.push(...detaileTargets) } - loaderCommonConfigToCrawlConfig(xCrawlConfig, advancedConfig, crawlDataConfig) + loaderCommonConfigToCrawlConfig( + xCrawlConfig, + advancedDetailTargetsConfig, + crawlDataConfig + ) return crawlDataConfig as CrawlDataConfig } @@ -548,49 +616,63 @@ function createCrawlFileConfig( xCrawlConfig: LoaderXCrawlConfig, originalConfig: UniteCrawlFileConfig ): CrawlFileConfig { - const crawlFileConfig: CrawlFileConfigOriginal = { + const crawlFileConfig: CrawlFileConfig = { detailTargets: [], intervalTime: undefined, + + selectFingerprintIndexs: [], + onBeforeSaveItemFile: undefined, onCrawlItemComplete: undefined } - let advancedConfig: CrawlFileAdvancedConfig = { targets: [] } + let advancedDetailTargetsConfig: CrawlFileAdvancedDetailTargetsConfig = { + targets: [], + detailTargets: [] + } if (isObject(originalConfig) && Object.hasOwn(originalConfig, 'targets')) { // CrawlFileAdvancedConfig 处理 const { targets } = originalConfig as CrawlFileAdvancedConfig - advancedConfig = originalConfig as CrawlFileAdvancedConfig - crawlFileConfig.detailTargets.push( - ...transformTargetToDetailTargets(targets) - ) + advancedDetailTargetsConfig = + originalConfig as CrawlFileAdvancedDetailTargetsConfig + + advancedDetailTargetsConfig.detailTargets = + transformTargetToDetailTargets(targets) } else { // CrawlFileDetailTargetConfig | CrawlFileDetailTargetConfig[] 处理 - crawlFileConfig.detailTargets.push( - ...(isArray(originalConfig) - ? originalConfig - : [originalConfig as CrawlFileDetailTargetConfig]) - ) + advancedDetailTargetsConfig.detailTargets = isArray(originalConfig) + ? originalConfig + : [originalConfig as CrawlFileDetailTargetConfig] } - loaderCommonConfigToCrawlConfig(xCrawlConfig, advancedConfig, crawlFileConfig) - - const haveAdvancedStoreDir = !isUndefined(advancedConfig?.storeDir) - const haveAdvancedExtension = !isUndefined(advancedConfig?.extension) + loaderCommonConfigToCrawlConfig( + xCrawlConfig, + advancedDetailTargetsConfig, + crawlFileConfig + ) + + const haveAdvancedStoreDir = !isUndefined( + advancedDetailTargetsConfig?.storeDir + ) + const haveAdvancedExtension = !isUndefined( + advancedDetailTargetsConfig?.extension + ) crawlFileConfig.detailTargets.forEach((detail) => { // 1.storeDir if (isUndefined(detail.storeDir) && haveAdvancedStoreDir) { - detail.storeDir = advancedConfig!.storeDir + detail.storeDir = advancedDetailTargetsConfig!.storeDir } // 2.extension if (isUndefined(detail.extension) && haveAdvancedExtension) { - detail.extension = advancedConfig!.extension + detail.extension = advancedDetailTargetsConfig!.extension } }) - crawlFileConfig.onBeforeSaveItemFile = advancedConfig.onBeforeSaveItemFile + crawlFileConfig.onBeforeSaveItemFile = + advancedDetailTargetsConfig.onBeforeSaveItemFile return crawlFileConfig as CrawlFileConfig } @@ -598,23 +680,32 @@ function createCrawlFileConfig( /* Single crawl handle */ async function pageSingleCrawlHandle( - detaileInfo: DetailInfo, + device: Device, extraConfig: ExtraPageConfig -): Promise { - const { id, detailTarget } = detaileInfo - const { errorPageMap, browser } = extraConfig - - const page = await browser.newPage() - - if (detailTarget.viewport) { - await page.setViewport(detailTarget.viewport) +) { + const { + detailTargetConfig, + detailTargetResult, + retryCount, + maxRetry, + crawlErrorQueue + } = device + const { browser } = extraConfig + const notAllowRetry = retryCount === maxRetry + + // 是否创建过 Page + const page = detailTargetResult?.page ?? (await browser.newPage()) + + if (detailTargetConfig.viewport) { + await page.setViewport(detailTargetConfig.viewport) } let response: HTTPResponse | null = null + let notError = true try { - if (detailTarget.proxy) { + if (detailTargetConfig.proxyUrl) { await browser.createIncognitoBrowserContext({ - proxyServer: detailTarget.proxy + proxyServer: detailTargetConfig.proxyUrl }) } else { await browser.createIncognitoBrowserContext({ @@ -622,84 +713,141 @@ async function pageSingleCrawlHandle( }) } - if (detailTarget.cookies) { - const cookies = parsePageCookies(detailTarget.url, detailTarget.cookies) + if (detailTargetConfig.cookies) { + const cookies = parsePageCookies( + detailTargetConfig.url, + detailTargetConfig.cookies + ) await page.setCookie(...cookies) } else { - const cookies = await page.cookies(detailTarget.url) + const cookies = await page.cookies(detailTargetConfig.url) await page.deleteCookie(...cookies) } - if (detailTarget.headers) { - await page.setExtraHTTPHeaders(detailTarget.headers) + if (detailTargetConfig.headers) { + await page.setExtraHTTPHeaders(detailTargetConfig.headers) } - response = await page.goto(detailTarget.url, { - timeout: detailTarget.timeout + response = await page.goto(detailTargetConfig.url, { + timeout: detailTargetConfig.timeout }) - } catch (error) { - // 收集报错的 page - if (!errorPageMap.get(id)) { - errorPageMap.set(id, page) - } - - // 让外面收集错误 - throw error + } catch (error: any) { + notError = false + crawlErrorQueue.push(error) } - return { response, page } + // 保存结果 + device.detailTargetResult = { response, page } + + // 处理结果 + const isStatusNormal = !isCrawlStatusInHttpStatus(device) + const isSuccess = notError && isStatusNormal + + device.isStatusNormal = isStatusNormal + device.isSuccess = isSuccess + if (isSuccess || notAllowRetry) { + device.isHandle = true + + pageSingleResultHandle(device, extraConfig) + } } async function dataAndFileSingleCrawlHandle( - detaileInfo: DetailInfo< - LoaderCrawlDataDetail | LoaderCrawlFileDetail, - Request - > + device: Device, + extraConfig: ExtraDataConfig | ExtraFileConfig ) { - const { detailTarget } = detaileInfo + const { detailTargetConfig, crawlErrorQueue, maxRetry, retryCount } = device + const notAllowRetry = maxRetry === retryCount + + let detailTargetResult = null + let notError = true + try { + detailTargetResult = await request(detailTargetConfig) + } catch (error: any) { + notError = false + crawlErrorQueue.push(error) + } + + // 保存结果 + device.detailTargetResult = detailTargetResult + + // 处理结果 + const isStatusNormal = !isCrawlStatusInHttpStatus(device) + const isSuccess = notError && isStatusNormal - return await request(detailTarget) + device.isStatusNormal = isStatusNormal + device.isSuccess = isSuccess + if (isSuccess || notAllowRetry) { + device.isHandle = true + + if (extraConfig.type === 'data') { + dataSingleResultHandle(device, extraConfig as ExtraDataConfig) + } else if (extraConfig.type === 'file') { + fileSingleResultHandle(device, extraConfig as ExtraFileConfig) + } + } } /* Single result handle */ +const resultEssentialOtherKeys = ['isSuccess', 'retryCount'] as const + +function handleResultEssentialOtherValue(device: any) { + Object.keys(device).forEach((key) => { + if (resultEssentialOtherKeys.includes(key as any)) { + device.result[key] = device[key] + } + }) +} function pageSingleResultHandle( - detaileInfo: DetailInfo, + device: Device, extraConfig: ExtraPageConfig ) { - const { id, isSuccess, detailTargetRes } = detaileInfo - const { errorPageMap, browser, onCrawlItemComplete } = extraConfig + const { detailTargetResult, result } = device + const { browser, onCrawlItemComplete } = extraConfig - let data: { - browser: Browser - response: HTTPResponse | null - page: Page - } | null = null + handleResultEssentialOtherValue(device) - if (isSuccess && detailTargetRes) { - data = { browser: browser!, ...detailTargetRes } - } else { - const page = errorPageMap.get(id)! + result.data = { browser, ...detailTargetResult } - data = { browser: browser!, response: null, page } + if (onCrawlItemComplete) { + onCrawlItemComplete(device.result as CrawlPageSingleResult) } +} + +function dataSingleResultHandle( + device: Device, + extraConfig: ExtraDataConfig +) { + const { isSuccess, detailTargetResult, result } = device + const { onCrawlItemComplete } = extraConfig + + handleResultEssentialOtherValue(device) - detaileInfo.data = data + if (isSuccess && detailTargetResult) { + const contentType = detailTargetResult.headers['content-type'] ?? '' - const crawlPageSingleRes: AnyObject = detaileInfo - delete crawlPageSingleRes.detailTarget - delete crawlPageSingleRes.detailTargetRes + const data = + contentType === 'application/json' + ? JSON.parse(detailTargetResult.data.toString()) + : contentType.includes('text') + ? detailTargetResult.data.toString() + : detailTargetResult.data + + result.data = { ...detailTargetResult, data } + } if (onCrawlItemComplete) { - onCrawlItemComplete(crawlPageSingleRes as CrawlPageSingleRes) + onCrawlItemComplete(result as CrawlDataSingleResult) } } function fileSingleResultHandle( - detaileInfo: DetailInfo, + device: Device, extraConfig: ExtraFileConfig ) { - const { id, isSuccess, detailTarget, detailTargetRes } = detaileInfo + const { id, isSuccess, detailTargetConfig, detailTargetResult, result } = + device const { saveFileErrorArr, saveFilePendingQueue, @@ -708,26 +856,28 @@ function fileSingleResultHandle( onBeforeSaveItemFile } = extraConfig - const crawlFileSingleRes: AnyObject = detaileInfo - delete crawlFileSingleRes.detailTarget - delete crawlFileSingleRes.detailTargetRes + handleResultEssentialOtherValue(device) - if (isSuccess && detailTargetRes) { - const mimeType = detailTargetRes.headers['content-type'] ?? '' + if (isSuccess && detailTargetResult) { + const mimeType = detailTargetResult.headers['content-type'] ?? '' - const fileName = detailTarget.fileName ?? `${id}-${new Date().getTime()}` + const fileName = + detailTargetConfig.fileName ?? `${id}-${new Date().getTime()}` const fileExtension = - detailTarget.extension ?? `.${mimeType.split('/').pop()}` + detailTargetConfig.extension ?? `.${mimeType.split('/').pop()}` - if (detailTarget.storeDir && !fs.existsSync(detailTarget.storeDir)) { - mkdirDirSync(detailTarget.storeDir) + if ( + detailTargetConfig.storeDir && + !fs.existsSync(detailTargetConfig.storeDir) + ) { + fs.mkdirSync(detailTargetConfig.storeDir, { recursive: true }) } - const storePath = detailTarget.storeDir ?? __dirname + const storePath = detailTargetConfig.storeDir ?? __dirname const filePath = path.resolve(storePath, fileName + fileExtension) // 在保存前的回调 - const data = detailTargetRes.data + const data = detailTargetResult.data let dataPromise = Promise.resolve(data) if (onBeforeSaveItemFile) { dataPromise = onBeforeSaveItemFile({ @@ -752,8 +902,8 @@ function fileSingleResultHandle( } const size = newData.length - detaileInfo.data = { - ...detailTargetRes, + result.data = { + ...detailTargetResult, data: { isSuccess, fileName, @@ -765,7 +915,7 @@ function fileSingleResultHandle( } if (onCrawlItemComplete) { - onCrawlItemComplete(crawlFileSingleRes as CrawlFileSingleRes) + onCrawlItemComplete(device.result as CrawlFileSingleResult) } }) @@ -773,7 +923,7 @@ function fileSingleResultHandle( saveFilePendingQueue.push(saveFileItemPending) } else { if (onCrawlItemComplete) { - onCrawlItemComplete(crawlFileSingleRes as CrawlFileSingleRes) + onCrawlItemComplete(device.result as CrawlFileSingleResult) } } } @@ -787,35 +937,35 @@ export function createCrawlPage(xCrawlConfig: LoaderXCrawlConfig) { function crawlPage( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (result: CrawlPageSingleResult) => void + ): Promise function crawlPage( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (result: CrawlPageSingleResult) => void + ): Promise function crawlPage( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (result: CrawlPageSingleResult[]) => void + ): Promise function crawlPage( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (result: CrawlPageSingleResult[]) => void + ): Promise async function crawlPage( config: UniteCrawlPageConfig, - callback?: (res: any) => void - ): Promise { + callback?: (result: any) => void + ): Promise { // 创建浏览器 if (!haveCreateBrowser) { haveCreateBrowser = true createBrowserPending = puppeteer .launch(xCrawlConfig.crawlPage?.launchBrowser) - .then((res) => { - browser = res + .then((result) => { + browser = result }) } @@ -831,31 +981,30 @@ export function createCrawlPage(xCrawlConfig: LoaderXCrawlConfig) { createCrawlPageConfig(xCrawlConfig, config) const extraConfig: ExtraPageConfig = { - errorPageMap: new Map(), + type: 'page', + browser: browser!, intervalTime, onCrawlItemComplete } - const crawlResArr = (await controller( - 'page', + const crawlResultArr = (await controller( xCrawlConfig.mode, detailTargets, extraConfig, - pageSingleCrawlHandle, - pageSingleResultHandle - )) as CrawlPageSingleRes[] + pageSingleCrawlHandle + )) as CrawlPageSingleResult[] - const crawlRes = + const crawlResult = isArray(config) || (isObject(config) && Object.hasOwn(config, 'targets')) - ? crawlResArr - : crawlResArr[0] + ? crawlResultArr + : crawlResultArr[0] if (callback) { - callback(crawlRes) + callback(crawlResult) } - return crawlRes + return crawlResult } return crawlPage @@ -864,81 +1013,54 @@ export function createCrawlPage(xCrawlConfig: LoaderXCrawlConfig) { export function createCrawlData(xCrawlConfig: LoaderXCrawlConfig) { function crawlData( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (result: CrawlDataSingleResult) => void + ): Promise> function crawlData( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (result: CrawlDataSingleResult) => void + ): Promise> function crawlData( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (result: CrawlDataSingleResult[]) => void + ): Promise[]> function crawlData( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (result: CrawlDataSingleResult[]) => void + ): Promise[]> async function crawlData( config: UniteCrawlDataConfig, - callback?: (res: any) => void - ): Promise | CrawlDataSingleRes[]> { + callback?: (result: any) => void + ): Promise | CrawlDataSingleResult[]> { const { detailTargets, intervalTime, onCrawlItemComplete } = createCrawlDataConfig(xCrawlConfig, config) - function dataSingleResultHandle( - detaileInfo: DetailInfo, - extraConfig: ExtraDataConfig - ) { - const { isSuccess, detailTargetRes } = detaileInfo - const { onCrawlItemComplete } = extraConfig - - if (isSuccess && detailTargetRes) { - const contentType = detailTargetRes.headers['content-type'] ?? '' - - const data: T = contentType.includes('text') - ? detailTargetRes.data.toString() - : JSON.parse(detailTargetRes.data.toString()) - - detaileInfo.data = { ...detailTargetRes, data } - } - - const crawlDataSingleRes: AnyObject = detaileInfo - delete crawlDataSingleRes.detailTarget - delete crawlDataSingleRes.detailTargetRes - - if (onCrawlItemComplete) { - onCrawlItemComplete(crawlDataSingleRes as CrawlDataSingleRes) - } - } - const extraConfig: ExtraDataConfig = { + type: 'data', intervalTime, onCrawlItemComplete } - const crawlResArr = (await controller( - 'data', + const crawlResultArr = (await controller( xCrawlConfig.mode, detailTargets, extraConfig, - dataAndFileSingleCrawlHandle, - dataSingleResultHandle - )) as CrawlDataSingleRes[] + dataAndFileSingleCrawlHandle + )) as CrawlDataSingleResult[] - const crawlRes = + const crawlResult = isArray(config) || (isObject(config) && Object.hasOwn(config, 'targets')) - ? crawlResArr - : crawlResArr[0] + ? crawlResultArr + : crawlResultArr[0] if (callback) { - callback(crawlRes) + callback(crawlResult) } - return crawlRes + return crawlResult } return crawlData @@ -947,23 +1069,23 @@ export function createCrawlData(xCrawlConfig: LoaderXCrawlConfig) { export function createCrawlFile(xCrawlConfig: LoaderXCrawlConfig) { function crawlFile( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (result: CrawlFileSingleResult) => void + ): Promise function crawlFile( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (result: CrawlFileSingleResult[]) => void + ): Promise function crawlFile( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (result: CrawlFileSingleResult[]) => void + ): Promise async function crawlFile( config: UniteCrawlFileConfig, - callback?: (res: any) => void - ): Promise { + callback?: (result: any) => void + ): Promise { const { detailTargets, intervalTime, @@ -972,6 +1094,8 @@ export function createCrawlFile(xCrawlConfig: LoaderXCrawlConfig) { } = createCrawlFileConfig(xCrawlConfig, config) const extraConfig: ExtraFileConfig = { + type: 'file', + saveFileErrorArr: [], saveFilePendingQueue: [], @@ -980,14 +1104,12 @@ export function createCrawlFile(xCrawlConfig: LoaderXCrawlConfig) { onBeforeSaveItemFile } - const crawlResArr = (await controller( - 'file', + const crawlResultArr = (await controller( xCrawlConfig.mode, detailTargets, extraConfig, - dataAndFileSingleCrawlHandle, - fileSingleResultHandle - )) as CrawlFileSingleRes[] + dataAndFileSingleCrawlHandle + )) as CrawlFileSingleResult[] const { saveFilePendingQueue, saveFileErrorArr } = extraConfig @@ -1000,39 +1122,39 @@ export function createCrawlFile(xCrawlConfig: LoaderXCrawlConfig) { // 统计保存 const succssIds: number[] = [] const errorIds: number[] = [] - crawlResArr.forEach((item) => { + crawlResultArr.forEach((item) => { if (item.data?.data.isSuccess) { succssIds.push(item.id) } else { errorIds.push(item.id) } }) - log('Save file final result:') + log(logStatistics('Save files finish:')) log( logSuccess( - ` Success - total: ${succssIds.length}, ids: [ ${succssIds.join( - ' - ' + ` Success - total: ${succssIds.length}, targets id: [ ${succssIds.join( + ', ' )} ]` ) ) log( logError( - ` Error - total: ${errorIds.length}, ids: [ ${errorIds.join( - ' - ' + ` Error - total: ${errorIds.length}, targets id: [ ${errorIds.join( + ', ' )} ]` ) ) - const crawlRes = + const crawlResult = isArray(config) || (isObject(config) && Object.hasOwn(config, 'targets')) - ? crawlResArr - : crawlResArr[0] + ? crawlResultArr + : crawlResultArr[0] if (callback) { - callback(crawlRes) + callback(crawlResult) } - return crawlRes + return crawlResult } return crawlFile @@ -1055,13 +1177,13 @@ export function startPolling( const intervalId = setInterval(startCallback, total) function startCallback() { - console.log(logSuccess(`Start the ${logWarn.bold(++count)} polling`)) + console.log(logStart(`Start polling - count: ${++count}`)) callback(count, stopPolling) } function stopPolling() { clearInterval(intervalId) - console.log(logSuccess(`Stop the polling`)) + console.log(logWarn(`Stop the polling`)) } } diff --git a/src/batchCrawl.ts b/src/batchCrawl.ts new file mode 100644 index 00000000..ac60b28d --- /dev/null +++ b/src/batchCrawl.ts @@ -0,0 +1,85 @@ +import { isNumber, isUndefined, log, logNumber, random, sleep } from './utils' + +import type { ExtraCommonConfig } from './api' +import { CrawlDetail, Device } from './controller' + +async function useSleepByBatch( + isHaventervalTime: boolean, + isNumberIntervalTime: boolean, + intervalTime: any, + id: number +) { + if (isHaventervalTime && id > 1) { + const timeout: number = isNumberIntervalTime + ? intervalTime + : random(intervalTime.max, intervalTime.min) + + log( + `Target id: ${logNumber(id)} - Sleep time: ${logNumber(timeout + 'ms')}` + ) + + await sleep(timeout) + } else { + log(`Target id: ${logNumber(id)} - Sleep time: ${logNumber('0ms')}`) + } +} + +export async function asyncBatchCrawl< + T extends CrawlDetail, + E extends ExtraCommonConfig, + R +>( + devices: Device[], + extraConfig: E, + singleCrawlHandle: (device: Device, extraConfig: E) => Promise +) { + const { intervalTime } = extraConfig + + const isHaventervalTime = !isUndefined(intervalTime) + const isNumberIntervalTime = isNumber(intervalTime) + + const crawlPendingQueue: Promise[] = [] + for (const device of devices) { + const { id } = device + + await useSleepByBatch( + isHaventervalTime, + isNumberIntervalTime, + intervalTime, + id + ) + + crawlPendingQueue.push(singleCrawlHandle(device, extraConfig)) + } + + // 等待所有爬取结束 + await Promise.all(crawlPendingQueue) +} + +export async function syncBatchCrawl< + T extends CrawlDetail, + E extends ExtraCommonConfig, + R +>( + devices: Device[], + extraConfig: E, + singleCrawlHandle: (device: Device, extraConfig: E) => Promise +) { + const { intervalTime } = extraConfig + + const isHaventervalTime = !isUndefined(intervalTime) + const isNumberIntervalTime = isNumber(intervalTime) + + for (const device of devices) { + const { id } = device + + await useSleepByBatch( + isHaventervalTime, + isNumberIntervalTime, + intervalTime, + id + ) + + await singleCrawlHandle(device, extraConfig) + } +} diff --git a/src/batchCrawlHandle.ts b/src/batchCrawlHandle.ts deleted file mode 100644 index dcb68fda..00000000 --- a/src/batchCrawlHandle.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { isNumber, isUndefined, log, logNumber, random, sleep } from './utils' - -import type { ExtraCommonConfig } from './api' -import type { DetailInfo, CrawlDetail } from './controller' - -async function useSleepByBatch( - isHaventervalTime: boolean, - isNumberIntervalTime: boolean, - intervalTime: any, - id: number -) { - if (isHaventervalTime && id > 1) { - const timeout: number = isNumberIntervalTime - ? intervalTime - : random(intervalTime.max, intervalTime.min) - - log( - `Id: ${logNumber(id)} - Crawl needs to sleep for ${logNumber( - timeout + 'ms' - )} milliseconds before sending` - ) - - await sleep(timeout) - } else { - log(`Id: ${logNumber(id)} - Crawl does not need to sleep, send immediately`) - } -} - -export async function asyncBatchCrawl< - T extends CrawlDetail, - E extends ExtraCommonConfig, - R ->( - detailInfos: DetailInfo[], - extraConfig: E, - singleCrawlHandle: ( - detailInfo: DetailInfo, - extraConfig: E - ) => Promise, - singleResultHandle: (detailInfo: DetailInfo, extraConfig: E) => void -) { - const { intervalTime } = extraConfig - - const isHaventervalTime = !isUndefined(intervalTime) - const isNumberIntervalTime = isNumber(intervalTime) - - const crawlPendingQueue: Promise[] = [] - for (const detaileInfo of detailInfos) { - const { id } = detaileInfo - - await useSleepByBatch( - isHaventervalTime, - isNumberIntervalTime, - intervalTime, - id - ) - - const crawlSinglePending = singleCrawlHandle(detaileInfo, extraConfig) - .catch((error) => { - detaileInfo.crawlErrorQueue.push(error) - return false - }) - .then((detailTargetRes) => { - if (typeof detailTargetRes === 'boolean') { - if (detaileInfo.retryCount === detaileInfo.maxRetry) { - singleResultHandle(detaileInfo, extraConfig) - } - - return - } - - detaileInfo.isSuccess = true - detaileInfo.detailTargetRes = detailTargetRes - - singleResultHandle(detaileInfo, extraConfig) - }) - - crawlPendingQueue.push(crawlSinglePending) - } - - // 等待所有爬取结束 - await Promise.all(crawlPendingQueue) -} - -export async function syncBatchCrawl< - T extends CrawlDetail, - E extends ExtraCommonConfig, - R ->( - detailInfos: DetailInfo[], - extraConfig: E, - singleCrawlHandle: ( - detaileInfo: DetailInfo, - extraConfig: E - ) => Promise, - singleResultHandle: (detaileInfo: DetailInfo, extraConfig: E) => void -) { - const { intervalTime } = extraConfig - - const isHaventervalTime = !isUndefined(intervalTime) - const isNumberIntervalTime = isNumber(intervalTime) - - for (const detailInfo of detailInfos) { - const { id } = detailInfo - - await useSleepByBatch( - isHaventervalTime, - isNumberIntervalTime, - intervalTime, - id - ) - - try { - detailInfo.detailTargetRes = await singleCrawlHandle( - detailInfo, - extraConfig - ) - detailInfo.isSuccess = true - } catch (error: any) { - detailInfo.crawlErrorQueue.push(error) - } - - if (detailInfo.isSuccess || detailInfo.retryCount === detailInfo.maxRetry) { - singleResultHandle(detailInfo, extraConfig) - } - } -} diff --git a/src/controller.ts b/src/controller.ts index 05297001..6fac256b 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -1,105 +1,200 @@ -import { asyncBatchCrawl, syncBatchCrawl } from './batchCrawlHandle' +import { asyncBatchCrawl, syncBatchCrawl } from './batchCrawl' import { priorityQueueMergeSort } from './sort' import { ExtraCommonConfig, LoaderCrawlDataDetail, LoaderCrawlFileDetail, - LoaderCrawlPageDetail + LoaderCrawlPageDetail, + ProxyDetails } from './api' -import { log, logError, logNumber, logSuccess, logWarn } from './utils' +import { + isObject, + isUndefined, + log, + logError, + logStart, + logStatistics, + logSuccess, + logWarn +} from './utils' +import { HTTPResponse } from 'puppeteer' +import { Request } from './request' +import { CrawlCommonResult } from './types/api' export type CrawlDetail = | LoaderCrawlPageDetail | LoaderCrawlDataDetail | LoaderCrawlFileDetail -export interface DetailInfo { +interface DeviceResult extends CrawlCommonResult { + data: any +} + +export interface Device { id: number + + isHandle: boolean isSuccess: boolean + isStatusNormal: boolean + + detailTargetConfig: T + detailTargetResult: R | null + maxRetry: number retryCount: number + proxyDetails: ProxyDetails crawlErrorQueue: Error[] - data: any | null - detailTarget: T - detailTargetRes: R | null + result: DeviceResult } -type TargetSingleRes = Omit< - DetailInfo, - 'detailTarget' | 'detailTargetRes' -> +export function isCrawlStatusInHttpStatus(device: Device) { + const { detailTargetConfig, detailTargetResult } = device + + let status: number | null = null + + if ( + isObject(detailTargetResult) && + Object.hasOwn(detailTargetResult, 'response') && + (detailTargetResult as any).response + ) { + // crawlPage + const response: HTTPResponse = (detailTargetResult as any).response + status = response.status() + } else if (isObject(detailTargetResult)) { + // crawlData / crawlFie + status = (detailTargetResult as any as Request).statusCode ?? null + } + + let result = false + const switchByHttpStatus = detailTargetConfig.proxy?.switchByHttpStatus + if (status && switchByHttpStatus && switchByHttpStatus.includes(status)) { + result = true + } + + return result +} export async function controller< T extends CrawlDetail, E extends ExtraCommonConfig, R >( - name: 'page' | 'data' | 'file', mode: 'async' | 'sync', detailTargets: T[], extraConfig: E, - singleCrawlHandle: ( - detailInfo: DetailInfo, - extraConfig: E - ) => Promise, - singleResultHandle: (detailInfo: DetailInfo, extraConfig: E) => void -): Promise { + singleCrawlHandle: (device: Device, extraConfig: E) => Promise +) { + const { type } = extraConfig + // 是否使用优先爬取 const isPriorityCrawl = !detailTargets.every( (item) => item.priority === detailTargets[0].priority ) const detailTargetConfigs = isPriorityCrawl ? priorityQueueMergeSort( - detailTargets.map((item) => ({ - ...item, - valueOf: () => item.priority - })) + detailTargets.map((item) => ({ ...item, valueOf: () => item.priority })) ) : detailTargets - // 通过映射生成新的配置数组 - const detailInfos: DetailInfo[] = detailTargetConfigs.map( - (detailTarget, index) => ({ - id: index + 1, - isSuccess: false, - maxRetry: detailTarget.maxRetry, - retryCount: 0, - crawlErrorQueue: [], - data: null, - - detailTarget, - detailTargetRes: null - }) + // 生成装置 + const devices: Device[] = detailTargetConfigs.map( + (detailTargetConfig, index) => { + const id = ++index + const { maxRetry, proxyDetails } = detailTargetConfig + const crawlErrorQueue: Error[] = [] + + return { + id, + + isHandle: false, + isSuccess: false, + isStatusNormal: false, + + detailTargetConfig, + detailTargetResult: null, + + maxRetry, + retryCount: 0, + proxyDetails, + crawlErrorQueue, + + result: { + id, + isSuccess: false, + maxRetry, + retryCount: 0, + proxyDetails, + crawlErrorQueue, + data: null + } + } + } ) log( - `${logSuccess(`Start crawling`)} - name: ${logWarn(name)}, mode: ${logWarn( - mode - )}, total: ${logNumber(detailInfos.length)} ` + logStart( + `Start crawling - type: ${type}, mode: ${mode}, total: ${devices.length}` + ) ) // 选择爬取模式 const batchCrawl = mode === 'async' ? asyncBatchCrawl : syncBatchCrawl let i = 0 - let crawlQueue: DetailInfo[] = detailInfos + let crawlQueue: Device[] = devices while (crawlQueue.length) { - await batchCrawl( - crawlQueue, - extraConfig, - singleCrawlHandle, - singleResultHandle - ) - - crawlQueue = crawlQueue.filter( - (config) => - config.maxRetry && - !config.isSuccess && - config.retryCount < config.maxRetry - ) + await batchCrawl(crawlQueue, extraConfig, singleCrawlHandle) + + crawlQueue = crawlQueue.filter((device) => { + const { + isHandle, + retryCount, + maxRetry, + detailTargetConfig, + proxyDetails, + crawlErrorQueue, + isStatusNormal + } = device + + // 没有被处理 / 没成功 / 状态码不符合 + let isRetry = false + const haveRetryChance = retryCount < maxRetry + if (!isHandle && haveRetryChance) { + isRetry = true + + // 轮换代理 + if (proxyDetails.length >= 2) { + // 状态码 / 失败次数 + const switchByErrorCount = + detailTargetConfig.proxy?.switchByErrorCount + if ( + !isStatusNormal || + (!isUndefined(switchByErrorCount) && + switchByErrorCount >= crawlErrorQueue.length) + ) { + // 设置当前代理 URL 状态 + proxyDetails.find( + (detail) => detail.url === detailTargetConfig.proxyUrl + )!.state = false + + // 寻找新代理 URL + const newProxyUrl = proxyDetails.find( + (detaile) => detaile.state + )?.url + + // 使用新代理 URL + if (!isUndefined(newProxyUrl)) { + detailTargetConfig.proxyUrl = newProxyUrl + } + } + } + } + + return isRetry + }) if (crawlQueue.length) { const retriedIds = crawlQueue.map((item) => { @@ -107,8 +202,13 @@ export async function controller< return item.id }) + log( - logWarn(`Retry: ${++i} - Ids to retry: [ ${retriedIds.join(' - ')} ]`) + logWarn( + `Start retrying - count: ${++i}, targets id: [ ${retriedIds.join( + ', ' + )} ]` + ) ) } } @@ -116,27 +216,29 @@ export async function controller< // 统计结果 const succssIds: number[] = [] const errorIds: number[] = [] - detailInfos.forEach((item) => { - if (item.isSuccess) { - succssIds.push(item.id) + devices.forEach((device) => { + if (device.isSuccess) { + succssIds.push(device.id) } else { - errorIds.push(item.id) + errorIds.push(device.id) } }) - log('Crawl the final result:') + log(logStatistics(`Crawl ${type}s finish:`)) log( logSuccess( - ` Success - total: ${succssIds.length}, ids: [ ${succssIds.join( - ' - ' + ` Success - total: ${succssIds.length}, targets id: [ ${succssIds.join( + ', ' )} ]` ) ) log( logError( - ` Error - total: ${errorIds.length}, ids: [ ${errorIds.join(' - ')} ]` + ` Error - total: ${errorIds.length}, targets id: [ ${errorIds.join( + ', ' + )} ]` ) ) - return detailInfos as TargetSingleRes[] + return devices.map((device) => device.result) } diff --git a/src/default.ts b/src/default.ts index 12acc3b2..3646b746 100644 --- a/src/default.ts +++ b/src/default.ts @@ -1,12 +1,58 @@ -export const randomFingerprint = { - platforms: [ - 'Android', - 'Chrome OS', - 'Chromium OS', - 'iOS', - 'Linux', - 'macOS', - 'Windows' - ], - mobiles: ['?0', '?1'] -} +import { DetailTargetFingerprintCommon } from './types/api' + +export const fingerprints: DetailTargetFingerprintCommon[] = [ + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 112, + minMajorVersion: 100, + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 } + ] + } + }, + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 91, + minMajorVersion: 88, + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 }, + { name: 'Edg', maxMinorVersion: 10, maxPatchVersion: 864 } + ] + } + }, + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', + versions: [ + { + name: 'Firefox', + maxMajorVersion: 47, + minMajorVersion: 43, + maxMinorVersion: 10, + maxPatchVersion: 5000 + } + ] + } + } +] diff --git a/src/request.ts b/src/request.ts index 9d194eac..a1342d3d 100644 --- a/src/request.ts +++ b/src/request.ts @@ -63,8 +63,8 @@ function handleRequestConfig( const isHttp = protocol === 'http:' const config: RequestOptions & MapTypeEmptyObject = { - agent: rawConfig.proxy - ? HttpsProxyAgent(rawConfig.proxy) + agent: rawConfig.proxyUrl + ? HttpsProxyAgent(rawConfig.proxyUrl) : isHttp ? new http.Agent() : new https.Agent(), diff --git a/src/types/api.ts b/src/types/api.ts index 438acfc1..2bae63a3 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -2,6 +2,7 @@ import { IncomingHttpHeaders } from 'node:http' import { Browser, HTTPResponse, Page, Protocol, Viewport } from 'puppeteer' import { AnyObject } from './common' +import { ProxyDetails } from '../api' /* API Config */ @@ -47,30 +48,34 @@ export type Platform = | 'Windows' | 'Unknown' -export type Mobile = '?0' | '?1' - export interface DetailTargetFingerprintCommon { - userAgent?: string ua?: string + mobile?: '?0' | '?1' | 'random' platform?: Platform platformVersion?: string - mobile?: Mobile acceptLanguage?: string -} - -export interface AdvancedFingerprintCommon { - userAgents?: string[] - uas?: string[] - platforms?: Platform[] - platformVersions?: string[] - mobiles?: Mobile[] - acceptLanguages?: string[] + userAgent?: { + value: string + versions?: { + name: string + maxMajorVersion?: number + minMajorVersion?: number + maxMinorVersion?: number + minMinorVersion?: number + maxPatchVersion?: number + minPatchVersion?: number + }[] + } } export interface CrawlCommonConfig { - timeout?: number - proxy?: string - maxRetry?: number + timeout?: number | null + proxy?: { + urls: string[] + switchByHttpStatus?: number[] + switchByErrorCount?: number + } | null + maxRetry?: number | null } // 1.Detail target @@ -82,9 +87,9 @@ export interface CrawlPageDetailTargetConfig extends CrawlCommonConfig { viewport?: Viewport | null fingerprint?: | (DetailTargetFingerprintCommon & { - maxWidth: number + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number }) | null @@ -114,40 +119,42 @@ export interface CrawlFileDetailTargetConfig extends CrawlCommonConfig { export interface CrawlPageAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlPageDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon & { - maxWidth: number + fingerprints?: (DetailTargetFingerprintCommon & { + maxWidth?: number minWidth?: number - maxHeight: number + maxHeight?: number minHidth?: number - } + })[] headers?: AnyObject cookies?: PageCookies viewport?: Viewport - onCrawlItemComplete?: (crawlPageSingleRes: CrawlPageSingleRes) => void + onCrawlItemComplete?: (crawlPageSingleResult: CrawlPageSingleResult) => void } export interface CrawlDataAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlDataDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject - onCrawlItemComplete?: (crawlDataSingleRes: CrawlDataSingleRes) => void + onCrawlItemComplete?: ( + crawlDataSingleResult: CrawlDataSingleResult + ) => void } export interface CrawlFileAdvancedConfig extends CrawlCommonConfig { targets: (string | CrawlFileDetailTargetConfig)[] intervalTime?: IntervalTime - fingerprint?: AdvancedFingerprintCommon + fingerprints?: DetailTargetFingerprintCommon[] headers?: AnyObject storeDir?: string extension?: string - onCrawlItemComplete?: (crawlFileSingleRes: CrawlFileSingleRes) => void + onCrawlItemComplete?: (crawlFileSingleResult: CrawlFileSingleResult) => void onBeforeSaveItemFile?: (info: { id: number fileName: string @@ -163,15 +170,16 @@ export interface StartPollingConfig { } /* API Result */ -export interface CrawlCommonRes { +export interface CrawlCommonResult { id: number isSuccess: boolean maxRetry: number retryCount: number + proxyDetails: ProxyDetails crawlErrorQueue: Error[] } -export interface CrawlPageSingleRes extends CrawlCommonRes { +export interface CrawlPageSingleResult extends CrawlCommonResult { data: { browser: Browser response: HTTPResponse | null @@ -179,7 +187,7 @@ export interface CrawlPageSingleRes extends CrawlCommonRes { } } -export interface CrawlDataSingleRes extends CrawlCommonRes { +export interface CrawlDataSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined headers: IncomingHttpHeaders @@ -187,7 +195,7 @@ export interface CrawlDataSingleRes extends CrawlCommonRes { } | null } -export interface CrawlFileSingleRes extends CrawlCommonRes { +export interface CrawlFileSingleResult extends CrawlCommonResult { data: { statusCode: number | undefined headers: IncomingHttpHeaders diff --git a/src/types/index.ts b/src/types/index.ts index d6e22f5e..eb6ff1f4 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,9 +2,9 @@ import { PuppeteerLaunchOptions } from 'puppeteer' import { StartPollingConfig, IntervalTime, - CrawlPageSingleRes, - CrawlDataSingleRes, - CrawlFileSingleRes, + CrawlPageSingleResult, + CrawlDataSingleResult, + CrawlFileSingleResult, CrawlFileAdvancedConfig, CrawlFileDetailTargetConfig, CrawlDataDetailTargetConfig, @@ -35,62 +35,62 @@ export interface XCrawlInstance { crawlPage: { ( config: string, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (result: CrawlPageSingleResult) => void + ): Promise ( config: CrawlPageDetailTargetConfig, - callback?: (res: CrawlPageSingleRes) => void - ): Promise + callback?: (result: CrawlPageSingleResult) => void + ): Promise ( config: (string | CrawlPageDetailTargetConfig)[], - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (result: CrawlPageSingleResult[]) => void + ): Promise ( config: CrawlPageAdvancedConfig, - callback?: (res: CrawlPageSingleRes[]) => void - ): Promise + callback?: (result: CrawlPageSingleResult[]) => void + ): Promise } crawlData: { ( config: CrawlDataDetailTargetConfig, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (result: CrawlDataSingleResult) => void + ): Promise> ( config: string, - callback?: (res: CrawlDataSingleRes) => void - ): Promise> + callback?: (result: CrawlDataSingleResult) => void + ): Promise> ( config: (string | CrawlDataDetailTargetConfig)[], - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (result: CrawlDataSingleResult[]) => void + ): Promise[]> ( config: CrawlDataAdvancedConfig, - callback?: (res: CrawlDataSingleRes[]) => void - ): Promise[]> + callback?: (result: CrawlDataSingleResult[]) => void + ): Promise[]> } crawlFile: { ( config: CrawlFileDetailTargetConfig, - callback?: (res: CrawlFileSingleRes) => void - ): Promise + callback?: (result: CrawlFileSingleResult) => void + ): Promise ( config: CrawlFileDetailTargetConfig[], - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (result: CrawlFileSingleResult[]) => void + ): Promise ( config: CrawlFileAdvancedConfig, - callback?: (res: CrawlFileSingleRes[]) => void - ): Promise + callback?: (result: CrawlFileSingleResult[]) => void + ): Promise } startPolling: ( diff --git a/src/utils.ts b/src/utils.ts index ec8f15a2..baa17017 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,34 +7,22 @@ export function sleep(timeout: number) { } export function random(max: number, min = 0) { - let res = Math.floor(Math.random() * max) + let result = Math.floor(Math.random() * max) - while (res < min) { - res = Math.floor(Math.random() * max) + while (result < min) { + result = Math.floor(Math.random() * max) } - return res -} - -export function mkdirDirSync(dir: string) { - const dirSplit = path.resolve(dir).split(path.sep) - - dirSplit.reduce((prev, item, index) => { - const currentDir = index !== 0 ? path.join(prev, item) : item - - if (!fs.existsSync(currentDir)) { - fs.mkdirSync(currentDir) - } - - return currentDir - }, '') + return result } export const log = console.log -export const logNumber = chalk.hex('#a57fff') +export const logStart = chalk.blueBright +export const logStatistics = chalk.whiteBright export const logSuccess = chalk.green export const logError = chalk.red export const logWarn = chalk.yellow +export const logNumber = chalk.hex('#a57fff') export function isUndefined(value: any): value is undefined { return typeof value === 'undefined' diff --git a/test/environment/api/crawlData.test.ts b/test/environment/api/crawlData.test.ts new file mode 100644 index 00000000..f797bc1a --- /dev/null +++ b/test/environment/api/crawlData.test.ts @@ -0,0 +1,32 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function testCrawlData() { + const testXCrawl = xCrawl() + + const res = await testXCrawl.crawlData({ + targets: ['http://localhost:8888', { url: 'http://localhost:8888' }] + }) + + return res.reduce((prev, item) => prev && item.isSuccess, true) +} + +test('crawlData', async () => { + console.log(chalk.bgGreen('================ crawlData ================')) + await expect(testCrawlData()).resolves.toBe(true) +}) diff --git a/test/environment/api/crawlFile.test.ts b/test/environment/api/crawlFile.test.ts new file mode 100644 index 00000000..51d941da --- /dev/null +++ b/test/environment/api/crawlFile.test.ts @@ -0,0 +1,44 @@ +import process from 'node:process' +import path from 'node:path' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +const urls: string[] = [ + 'https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area/4401.jpg', + 'https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area/4403.jpg' +] + +const storeDir = path.resolve(__dirname, './upload') + +async function testCrawlFile() { + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) + + const res = await testXCrawl.crawlFile({ + targets: urls, + storeDir + }) + + return res.reduce( + (prev, item) => prev && item.isSuccess && !!item.data?.data.isSuccess, + true + ) +} + +test('crawlFile', async () => { + console.log(chalk.bgGreen('================ crawlFile ================')) + await expect(testCrawlFile()).resolves.toBe(true) +}) diff --git a/test/environment/api/crawlPage.test.ts b/test/environment/api/crawlPage.test.ts new file mode 100644 index 00000000..47b46fdb --- /dev/null +++ b/test/environment/api/crawlPage.test.ts @@ -0,0 +1,38 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function testCrawlPage() { + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) + + const res = await testXCrawl.crawlPage({ + targets: [ + 'https://github.com/coder-hxl/x-crawl', + { url: 'https://github.com/coder-hxl/x-crawl' } + ] + }) + + await res[0].data.browser.close() + + return res.reduce((prev, item) => prev && item.isSuccess, true) +} + +test('crawlPage', async () => { + console.log(chalk.bgGreen('================ crawlPage ================')) + await expect(testCrawlPage()).resolves.toBe(true) +}) diff --git a/test/environment/startPolling.test.ts b/test/environment/api/startPolling.test.ts similarity index 85% rename from test/environment/startPolling.test.ts rename to test/environment/api/startPolling.test.ts index 218a8efb..d0a3b306 100644 --- a/test/environment/startPolling.test.ts +++ b/test/environment/api/startPolling.test.ts @@ -2,16 +2,17 @@ import process from 'node:process' import { expect, test } from '@jest/globals' import chalk from 'chalk' -import IXCrawl from '../../src' +import IXCrawl from 'src/' + const args = process.argv.slice(3) const environment = args[0] let xCrawl: typeof IXCrawl if (environment === 'dev') { - xCrawl = require('../../src').default + xCrawl = require('src/').default } else if (environment === 'pro') { - xCrawl = require('../../publish/dist') + xCrawl = require('publish/dist') } function startPolling() { diff --git a/test/environment/arguments/fingerprint.test.ts b/test/environment/arguments/fingerprint.test.ts new file mode 100644 index 00000000..0cc269a0 --- /dev/null +++ b/test/environment/arguments/fingerprint.test.ts @@ -0,0 +1,87 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function fingerprint() { + const testXCrawl = xCrawl() + + const res = await testXCrawl.crawlPage({ + targets: [ + 'http://localhost:8888', + { url: 'http://localhost:8888', fingerprint: null }, + { + url: 'http://localhost:8888', + fingerprint: { + maxWidth: 1024, + maxHeight: 800, + ua: `Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99`, + mobile: 'random', + platform: 'Windows', + platformVersion: '10', + acceptLanguage: `zh-CN,zh;q=0.9,en;q=0.8`, + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + maxMinorVersion: 10, + maxPatchVersion: 5615 + }, + { name: 'Safari', maxMinorVersion: 36, maxPatchVersion: 2333 } + ] + } + } + } + ], + fingerprints: [ + { + platform: 'Windows', + mobile: 'random', + userAgent: { + value: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + versions: [ + { + name: 'Chrome', + maxMajorVersion: 112, + minMajorVersion: 100, + maxMinorVersion: 20, + maxPatchVersion: 5000 + }, + { + name: 'Safari', + maxMajorVersion: 537, + minMajorVersion: 500, + maxMinorVersion: 36, + maxPatchVersion: 5000 + } + ] + } + } + ] + }) + + res[0].data.browser.close() + + return res.reduce((prev, item) => prev && item.isSuccess, true) +} + +test('fingerprint', async () => { + console.log(chalk.bgGreen('================ fingerprint ================')) + await expect(fingerprint()).resolves.toBe(true) +}) diff --git a/test/environment/arguments/mode.test.ts b/test/environment/arguments/mode.test.ts new file mode 100644 index 00000000..d40fc036 --- /dev/null +++ b/test/environment/arguments/mode.test.ts @@ -0,0 +1,49 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function async() { + const testXCrawl = xCrawl() + + const res = await testXCrawl.crawlData([ + 'http://localhost:8888', + 'http://localhost:8888' + ]) + + return res.reduce((prev, item) => prev && item.isSuccess, true) +} + +async function sync() { + const testXCrawl = xCrawl({ mode: 'sync' }) + + const res = await testXCrawl.crawlData([ + 'http://localhost:8888', + 'http://localhost:8888' + ]) + + return res.reduce((prev, item) => prev && item.isSuccess, true) +} + +test('mode - async', async () => { + console.log(chalk.bgGreen('================ mode - async ================')) + await expect(async()).resolves.toBe(true) +}) + +test('mode - sync', async () => { + console.log(chalk.bgGreen('================ mode - sync ================')) + await expect(sync()).resolves.toBe(true) +}) diff --git a/test/environment/arguments/proxy.test.ts b/test/environment/arguments/proxy.test.ts new file mode 100644 index 00000000..536da088 --- /dev/null +++ b/test/environment/arguments/proxy.test.ts @@ -0,0 +1,43 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function proxy() { + const testXCrawl = xCrawl() + + const res = await testXCrawl.crawlPage({ + targets: ['https://', 'https://github.com/coder-hxl'], + maxRetry: 3, + proxy: { + urls: ['http://localhost:129032', 'http://localhost:14892'], + switchByErrorCount: 2 + } + }) + + await res[0].data.browser.close() + + return ( + res[0].proxyDetails[0].state === false && + res[1].isSuccess && + res[1].proxyDetails[1].state === true + ) +} + +test('proxy', async () => { + console.log(chalk.bgGreen('================ proxy ================')) + await expect(proxy()).resolves.toBe(true) +}) diff --git a/test/environment/functions/errorCollect.test.ts b/test/environment/functions/errorCollect.test.ts new file mode 100644 index 00000000..d99b34dc --- /dev/null +++ b/test/environment/functions/errorCollect.test.ts @@ -0,0 +1,37 @@ +import process from 'node:process' +import { expect, test, jest } from '@jest/globals' +import chalk from 'chalk' + +import IXCrawl from 'src/' + +const args = process.argv.slice(3) +const environment = args[0] + +let xCrawl: typeof IXCrawl +if (environment === 'dev') { + xCrawl = require('src/').default +} else if (environment === 'pro') { + xCrawl = require('publish/dist') +} + +jest.setTimeout(60000) + +async function errorCollect() { + const testXCrawl = xCrawl({ maxRetry: 2 }) + + const res = await testXCrawl.crawlPage(['https://', 'https://', 'https://']) + + await res[0].data.browser.close() + + const errorCount = res.reduce( + (prev, curr) => prev + curr.crawlErrorQueue.length, + 0 + ) + + return errorCount === 9 +} + +test('errorCollect', async () => { + console.log(chalk.bgGreen('================ errorCollect ================')) + await expect(errorCollect()).resolves.toBe(true) +}) diff --git a/test/environment/test.ts b/test/environment/test.ts index e1048a52..39b9ba06 100644 --- a/test/environment/test.ts +++ b/test/environment/test.ts @@ -1,4 +1,14 @@ -import './crawlPage.test' -import './crawlData.test' -import './crawlFile.test' -import './startPolling.test' +import './arguments/mode.test' +import './arguments/fingerprint.test' +import './arguments/proxy.test' + +import './written/crawlPage.test' +import './written/crawlData.test' +import './written/crawlFile.test' + +import './functions/errorCollect.test' + +import './api/crawlPage.test' +import './api/crawlData.test' +import './api/crawlFile.test' +import './api/startPolling.test' diff --git a/test/environment/crawlData.test.ts b/test/environment/written/crawlData.test.ts similarity index 80% rename from test/environment/crawlData.test.ts rename to test/environment/written/crawlData.test.ts index 3f61f578..5a4c85b2 100644 --- a/test/environment/crawlData.test.ts +++ b/test/environment/written/crawlData.test.ts @@ -2,16 +2,16 @@ import process from 'node:process' import { expect, test, jest } from '@jest/globals' import chalk from 'chalk' -import IXCrawl from '../../src' +import IXCrawl from 'src/' const args = process.argv.slice(3) const environment = args[0] let xCrawl: typeof IXCrawl if (environment === 'dev') { - xCrawl = require('../../src').default + xCrawl = require('src/').default } else if (environment === 'pro') { - xCrawl = require('../../publish/dist') + xCrawl = require('publish/dist') } jest.setTimeout(60000) @@ -21,9 +21,7 @@ jest.setTimeout(60000) async function writtenString() { const testXCrawl = xCrawl() - const res = await testXCrawl.crawlData( - 'http://localhost:9001/api/room/193581217' - ) + const res = await testXCrawl.crawlData('http://localhost:8888') return res.isSuccess } @@ -33,7 +31,7 @@ async function writtenCrawlDataDetailConfig() { const testXCrawl = xCrawl() const res = await testXCrawl.crawlData({ - url: 'http://localhost:9001/api/room/193581217' + url: 'http://localhost:8888' }) return res.isSuccess @@ -44,8 +42,8 @@ async function writtenStringAndCrawlDataDetailConfigArr() { const testXCrawl = xCrawl() const res = await testXCrawl.crawlData([ - 'http://localhost:9001/api/room/193581217', - { url: 'http://localhost:9001/api/room/193581217' } + 'http://localhost:8888', + { url: 'http://localhost:8888' } ]) return res.reduce((prev, item) => prev && item.isSuccess, true) @@ -56,10 +54,7 @@ async function writtenCrawlDataAdvancedConfig() { const testXCrawl = xCrawl() const res = await testXCrawl.crawlData({ - targets: [ - 'http://localhost:9001/api/room/193581217', - { url: 'http://localhost:9001/api/room/193581217' } - ] + targets: ['http://localhost:8888', { url: 'http://localhost:8888' }] }) return res.reduce((prev, item) => prev && item.isSuccess, true) @@ -69,14 +64,14 @@ async function writtenCrawlDataAdvancedConfig() { // 2.1.Loader Base Config async function loaderBaseConfig() { const testXCrawl = xCrawl({ - baseUrl: 'http://localhost:9001/api', - proxy: 'http://localhost:14892', + baseUrl: 'http://localhost:8888', + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, intervalTime: { max: 1000 }, maxRetry: 0 }) - const res = await testXCrawl.crawlData(['/room/193581217', '/room/193581217']) + const res = await testXCrawl.crawlData(['/', '/']) return res.reduce((prev, item) => prev && item.isSuccess, true) } @@ -84,12 +79,12 @@ async function loaderBaseConfig() { // 2.2.Loader Advanced Config async function loaderAdvancedConfig() { const testXCrawl = xCrawl({ - baseUrl: 'http://localhost:9001/api' + baseUrl: 'http://localhost:8888' }) const res = await testXCrawl.crawlData({ - targets: ['/room/193581217', '/room/193581217'], - proxy: 'http://localhost:14892', + targets: ['/', '/'], + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, intervalTime: { max: 1000 }, maxRetry: 0 diff --git a/test/environment/crawlFile.test.ts b/test/environment/written/crawlFile.test.ts similarity index 90% rename from test/environment/crawlFile.test.ts rename to test/environment/written/crawlFile.test.ts index f1ce850a..928f3229 100644 --- a/test/environment/crawlFile.test.ts +++ b/test/environment/written/crawlFile.test.ts @@ -3,16 +3,16 @@ import path from 'node:path' import { expect, test, jest } from '@jest/globals' import chalk from 'chalk' -import IXCrawl from '../../src' +import IXCrawl from 'src/' const args = process.argv.slice(3) const environment = args[0] let xCrawl: typeof IXCrawl if (environment === 'dev') { - xCrawl = require('../../src').default + xCrawl = require('src/').default } else if (environment === 'pro') { - xCrawl = require('../../publish/dist') + xCrawl = require('publish/dist') } jest.setTimeout(60000) @@ -27,7 +27,7 @@ const storeDir = path.resolve(__dirname, './upload') /* 1.Written */ // 1.1.written CrawlFileDetailConfig async function writtenCrawlFileDetailConfig() { - const testXCrawl = xCrawl({ proxy: 'http://localhost:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlFile({ url: urls[0], storeDir }) @@ -36,7 +36,7 @@ async function writtenCrawlFileDetailConfig() { // 1.2.written CrawlFileDetailConfig[] async function writtenCrawlFileDetailConfigArr() { - const testXCrawl = xCrawl({ proxy: 'http://localhost:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlFile(urls.map((url) => ({ url, storeDir }))) @@ -48,7 +48,7 @@ async function writtenCrawlFileDetailConfigArr() { // 1.3.written CrawlFileAdvancedConfig async function writtenCrawlFileAdvancedConfig() { - const testXCrawl = xCrawl({ proxy: 'http://localhost:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlFile({ targets: urls, @@ -67,7 +67,7 @@ async function loaderBaseConfig() { const testXCrawl = xCrawl({ baseUrl: 'https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area', - proxy: 'http://localhost:14892', + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, intervalTime: { max: 1000 }, maxRetry: 0 @@ -90,7 +90,7 @@ async function loaderAdvancedConfig() { const res = await testXCrawl.crawlFile({ targets: ['/4401.jpg', '/4403.jpg'], - proxy: 'http://localhost:14892', + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, storeDir, intervalTime: { max: 1000 }, @@ -105,7 +105,7 @@ async function storeConfig() { const testXCrawl = xCrawl({ baseUrl: 'https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area', - proxy: 'http://localhost:14892' + proxy: { urls: ['http://localhost:14892'] } }) const record: string[] = [] diff --git a/test/environment/crawlPage.test.ts b/test/environment/written/crawlPage.test.ts similarity index 90% rename from test/environment/crawlPage.test.ts rename to test/environment/written/crawlPage.test.ts index 13e82ab6..cbe36ff3 100644 --- a/test/environment/crawlPage.test.ts +++ b/test/environment/written/crawlPage.test.ts @@ -2,16 +2,16 @@ import process from 'node:process' import { expect, test, jest } from '@jest/globals' import chalk from 'chalk' -import IXCrawl from '../../src' +import IXCrawl from 'src/' const args = process.argv.slice(3) const environment = args[0] let xCrawl: typeof IXCrawl if (environment === 'dev') { - xCrawl = require('../../src').default + xCrawl = require('src/').default } else if (environment === 'pro') { - xCrawl = require('../../publish/dist') + xCrawl = require('publish/dist') } jest.setTimeout(60000) @@ -30,7 +30,7 @@ async function writtenString() { // 1.2.written CrawlPageDetailConfig async function writtenCrawlPageDetailConfig() { - const testXCrawl = xCrawl({ proxy: 'http://localohst:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlPage({ url: 'https://github.com/coder-hxl/x-crawl' @@ -43,7 +43,7 @@ async function writtenCrawlPageDetailConfig() { // 1.3.written (string | CrawlPageDetailConfig)[] async function writtenStringAndCrawlPageDetailConfigArr() { - const testXCrawl = xCrawl({ proxy: 'http://localohst:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlPage([ 'https://github.com/coder-hxl/x-crawl', @@ -57,7 +57,7 @@ async function writtenStringAndCrawlPageDetailConfigArr() { // 1.4.written CrawlPageAdvancedConfig async function writtenCrawlPageAdvancedConfig() { - const testXCrawl = xCrawl({ proxy: 'http://localohst:14892' }) + const testXCrawl = xCrawl({ proxy: { urls: ['http://localhost:14892'] } }) const res = await testXCrawl.crawlPage({ targets: [ @@ -76,7 +76,7 @@ async function writtenCrawlPageAdvancedConfig() { async function loaderBaseConfig() { const testXCrawl = xCrawl({ baseUrl: 'https://github.com', - proxy: 'http://localhost:14892', + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, intervalTime: { max: 1000 }, maxRetry: 0 @@ -95,7 +95,7 @@ async function loaderAdvancedConfig() { const res = await testXCrawl.crawlPage({ targets: ['/coder-hxl', '/coder-hxl/x-crawl'], - proxy: 'http://localhost:14892', + proxy: { urls: ['http://localhost:14892'] }, timeout: 10000, intervalTime: { max: 1000 }, maxRetry: 0 diff --git a/test/server/index.js b/test/server/index.js new file mode 100644 index 00000000..2364a48f --- /dev/null +++ b/test/server/index.js @@ -0,0 +1 @@ +"use strict";require("node:http").createServer(((e,t)=>{t.setHeader("Content-Type","text/plain"),t.end("success")})).listen(8888,(()=>{console.log("服务器在 8888 端口启动成功~")})); diff --git a/test/server/index.ts b/test/server/index.ts new file mode 100644 index 00000000..6608c890 --- /dev/null +++ b/test/server/index.ts @@ -0,0 +1,10 @@ +import http from 'node:http' + +http + .createServer((req, res) => { + res.setHeader('Content-Type', 'text/plain') + res.end('success') + }) + .listen(8888, () => { + console.log(`服务器在 8888 端口启动成功~`) + }) diff --git a/test/start/index.js b/test/start/index.js index 568522c4..8cde044e 100644 --- a/test/start/index.js +++ b/test/start/index.js @@ -1 +1 @@ -"use strict";var e=require("node:fs"),t=require("node:fs/promises"),r=require("node:path"),o=require("puppeteer"),a=require("chalk"),n=require("node:http"),i=require("node:https"),s=require("node:url"),l=require("https-proxy-agent"),c=require("sharp"),u=require("path");function d(e,t=0){let r=Math.floor(Math.random()*e);for(;r1){const e=t?r:d(r.max,r.min);p(`Id: ${m(o)} - Crawl needs to sleep for ${m(e+"ms")} milliseconds before sending`),await function(e){return new Promise((t=>setTimeout(t,e)))}(e)}else p(`Id: ${m(o)} - Crawl does not need to sleep, send immediately`)}async function x(e,t,r,o){const{intervalTime:a}=t,n=!w(a),i=y(a),s=[];for(const l of e){const{id:e}=l;await C(n,i,a,e);const c=r(l,t).catch((e=>(l.crawlErrorQueue.push(e),!1))).then((e=>{"boolean"!=typeof e?(l.isSuccess=!0,l.detailTargetRes=e,o(l,t)):l.retryCount===l.maxRetry&&o(l,t)}));s.push(c)}await Promise.all(s)}async function S(e,t,r,o){const{intervalTime:a}=t,n=!w(a),i=y(a);for(const s of e){const{id:e}=s;await C(n,i,a,e);try{s.detailTargetRes=await r(s,t),s.isSuccess=!0}catch(e){s.crawlErrorQueue.push(e)}(s.isSuccess||s.retryCount===s.maxRetry)&&o(s,t)}}function b(e,t,r){const o=e[t];e[t]=e[r],e[r]=o}function $(e){if(1===e.length)return e;const t=Math.floor(e.length/2),r=$(e.slice(0,t)),o=$(e.slice(t)),a=[];let n=0,i=0;for(;n=o[i]?(a.push(r[n]),n++):(a.push(o[i]),i++);return ne.priority===r[0].priority))?$(r.map((e=>({...e,valueOf:()=>e.priority})))):r).map(((e,t)=>({id:t+1,isSuccess:!1,maxRetry:e.maxRetry,retryCount:0,crawlErrorQueue:[],data:null,detailTarget:e,detailTargetRes:null})));p(`${h("Start crawling")} - name: ${f(e)}, mode: ${f(t)}, total: ${m(i.length)} `);const s="async"===t?x:S;let l=0,c=i;for(;c.length;)if(await s(c,o,a,n),c=c.filter((e=>e.maxRetry&&!e.isSuccess&&e.retryCount(e.retryCount++,e.id)));p(f(`Retry: ${++l} - Ids to retry: [ ${e.join(" - ")} ]`))}const u=[],d=[];return i.forEach((e=>{e.isSuccess?u.push(e.id):d.push(e.id)})),p("Crawl the final result:"),p(h(` Success - total: ${u.length}, ids: [ ${u.join(" - ")} ]`)),p(g(` Error - total: ${d.length}, ids: [ ${d.join(" - ")} ]`)),i}function I(e,t){let r=e?`${e}`:"?";if(t)for(const e in t){r+=`&${e}=${t[e]}`}else r=e;return r}function O(e){const{protocol:t,hostname:r,port:o,pathname:a,search:c}=new s.URL(e.url),u="http:"===t,d={agent:e.proxy?l(e.proxy):u?new n.Agent:new i.Agent,protocol:t,hostname:r,port:o,path:a,search:I(c,e.params),method:e.method?.toLocaleUpperCase()??"GET",headers:{},timeout:e.timeout};return d.headers=function(e,t){const r={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",...e.headers??{}};return"POST"===t.method&&e.data&&(r["Content-Type"]="application/json",r["Content-Length"]=Buffer.byteLength(e.data)),r}(e,d),d}const E={platforms:["Android","Chrome OS","Chromium OS","iOS","Linux","macOS","Windows"],mobiles:["?0","?1"]};function F(e){return T(e)?e.map((e=>v(e)?e:{url:e})):[v(e)?e:{url:e}]}function j(e,t){const{userAgent:r,ua:o,platform:a,platformVersion:n,mobile:i,acceptLanguage:s}=t;let l=e.headers;l||(e.headers=l={}),r&&(l["user-agent"]=r),o&&(l["sec-ch-ua"]=o),a&&(l["sec-ch-platform"]=a),n&&(l["sec-ch-ua-platform-version"]=n),i&&(l["sec-ch-mobile"]=i),s&&(l["accept-language"]=s)}function P(e,t){const{maxWidth:r,minWidth:o,maxHeight:a,minHidth:n}=t,i=r===o?r:d(r,o),s=a===n?a:d(a,n),l=e.viewport;l?(l.width=i,l.height=s):e.viewport={width:i,height:s}}function A(e,t,r){r.detailTargets.forEach((r=>{const{url:o,timeout:a,proxy:n,maxRetry:i,priority:s,headers:l,fingerprint:c}=r;if(w(e.baseUrl)||(r.url=e.baseUrl+o),w(a)&&(w(t.timeout)?r.timeout=e.timeout:r.timeout=t.timeout),w(n)&&(w(t.proxy)?w(e.proxy)||(r.proxy=e.proxy):r.proxy=t.proxy),w(i)&&(w(t.maxRetry)?r.maxRetry=e.maxRetry:r.maxRetry=t.maxRetry),w(s)&&(r.priority=0),w(l)&&t.headers&&(r.headers={...t.headers}),c)j(r,c);else if(w(c)&&t.fingerprint){const{userAgents:e,uas:o,platforms:a,platformVersions:n,mobiles:i,acceptLanguages:s}=t.fingerprint;j(r,{userAgent:e?e[d(e.length)]:void 0,ua:o?o[d(o.length)]:void 0,platform:a?a[d(a.length)]:void 0,platformVersion:n?n[d(n.length)]:void 0,mobile:i?i[d(i.length)]:void 0,acceptLanguage:s?s[d(s.length)]:void 0})}else if(e.enableRandomFingerprint){const{platforms:e,mobiles:t}=E;j(r,{userAgent:`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.${d(10)}.${d(1e4)}.${d(1e3)} Safari/537.36`,platform:e[d(e.length)],mobile:t[d(t.length)]})}})),r.intervalTime=t.intervalTime,w(t.intervalTime)&&!w(e.intervalTime)&&(r.intervalTime=e.intervalTime),r.onCrawlItemComplete=t.onCrawlItemComplete}async function k(e,t){const{id:r,detailTarget:o}=e,{errorPageMap:a,browser:n}=t,i=await n.newPage();o.viewport&&await i.setViewport(o.viewport);let s=null;try{if(o.proxy?await n.createIncognitoBrowserContext({proxyServer:o.proxy}):await n.createIncognitoBrowserContext({proxyServer:void 0}),o.cookies){const e=function(e,t){const r=[];return"string"==typeof t?t.split("; ").forEach((t=>{const o=t.split("=");r.push({name:o[0],value:o[1],url:e})})):Array.isArray(t)?t.forEach((t=>{t.url||(t.url=e),r.push(t)})):"object"==typeof t&&t&&(t.url||(t.url=e),r.push(t)),r}(o.url,o.cookies);await i.setCookie(...e)}else{const e=await i.cookies(o.url);await i.deleteCookie(...e)}o.headers&&await i.setExtraHTTPHeaders(o.headers),s=await i.goto(o.url,{timeout:o.timeout})}catch(e){throw a.get(r)||a.set(r,i),e}return{response:s,page:i}}async function q(e){const{detailTarget:t}=e;return await(r=t,new Promise(((e,t)=>{const o=w(r.data);r.data=o?r.data:JSON.stringify(r.data);const a=O(r);function s(t){const{statusCode:r,headers:o}=t,a=[];t.on("data",(e=>a.push(e))),t.on("end",(()=>{const t=Buffer.concat(a);e({statusCode:r,headers:o,data:t})}))}let l;l="http:"===a.protocol?n.request(a,s):i.request(a,s),l.on("timeout",(()=>{t(new Error(`Timeout ${r.timeout}ms`))})),l.on("error",(e=>{t(e)})),"POST"!==a.method||o||l.write(r.data),l.end()})));var r}function B(e,t){const{id:r,isSuccess:o,detailTargetRes:a}=e,{errorPageMap:n,browser:i,onCrawlItemComplete:s}=t;let l=null;if(o&&a)l={browser:i,...a};else{l={browser:i,response:null,page:n.get(r)}}e.data=l;const c=e;delete c.detailTarget,delete c.detailTargetRes,s&&s(c)}function M(o,a){const{id:n,isSuccess:i,detailTarget:s,detailTargetRes:l}=o,{saveFileErrorArr:c,saveFilePendingQueue:u,onCrawlItemComplete:d,onBeforeSaveFile:p}=a,m=o;if(delete m.detailTarget,delete m.detailTargetRes,i&&l){const a=l.headers["content-type"]??"",i=s.fileName??`${n}-${(new Date).getTime()}`,g=s.extension??`.${a.split("/").pop()}`;s.storeDir&&!e.existsSync(s.storeDir)&&(h=s.storeDir,r.resolve(h).split(r.sep).reduce(((t,o,a)=>{const n=0!==a?r.join(t,o):o;return e.existsSync(n)||e.mkdirSync(n),n}),""));const f=s.storeDir??__dirname,w=r.resolve(f,i+g),y=l.data;let v=Promise.resolve(y);p&&(v=p({id:n,fileName:i,filePath:w,data:y}));const T=v.then((async e=>{let r=!0;try{await t.writeFile(w,e)}catch(e){r=!1;const t=`File save error at id ${n}: ${e.message}`,o=()=>n;c.push({message:t,valueOf:o})}const s=e.length;o.data={...l,data:{isSuccess:r,fileName:i,fileExtension:g,mimeType:a,size:s,filePath:w}},d&&d(m)}));u.push(T)}else d&&d(m);var h}function D(e){let t=null,r=null,a=!1;return async function(n,i){a||(a=!0,r=o.launch(e.crawlPage?.launchBrowser).then((e=>{t=e}))),r&&(await r,r&&(r=null));const{detailTargets:s,intervalTime:l,onCrawlItemComplete:c}=function(e,t){const r={detailTargets:[],intervalTime:void 0,onCrawlItemComplete:void 0};let o={targets:[]};if(v(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;o=t,r.detailTargets.push(...F(e))}else{const e=F(t);r.detailTargets.push(...e)}return A(e,o,r),r.detailTargets.forEach((e=>{const{cookies:t,viewport:r,fingerprint:a}=e;w(t)&&o.cookies&&(e.cookies=o.cookies),w(r)&&o.viewport&&(e.viewport=o.viewport),a?P(e,a):w(a)&&o.fingerprint&&P(e,o.fingerprint)})),r}(e,n),u={errorPageMap:new Map,browser:t,intervalTime:l,onCrawlItemComplete:c},d=await R("page",e.mode,s,u,k,B),p=T(n)||v(n)&&Object.hasOwn(n,"targets")?d:d[0];return i&&i(p),p}}function L(e){return async function(t,r){const{detailTargets:o,intervalTime:a,onCrawlItemComplete:n}=function(e,t){const r={detailTargets:[],intervalTime:void 0,onCrawlItemComplete:void 0};let o={targets:[]};if(v(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;o=t,r.detailTargets.push(...F(e))}else{const e=F(t);r.detailTargets.push(...e)}return A(e,o,r),r}(e,t),i={intervalTime:a,onCrawlItemComplete:n},s=await R("data",e.mode,o,i,q,(function(e,t){const{isSuccess:r,detailTargetRes:o}=e,{onCrawlItemComplete:a}=t;if(r&&o){const t=(o.headers["content-type"]??"").includes("text")?o.data.toString():JSON.parse(o.data.toString());e.data={...o,data:t}}const n=e;delete n.detailTarget,delete n.detailTargetRes,a&&a(n)})),l=T(t)||v(t)&&Object.hasOwn(t,"targets")?s:s[0];return r&&r(l),l}}function W(e){return async function(t,r){const{detailTargets:o,intervalTime:a,onBeforeSaveFile:n,onCrawlItemComplete:i}=function(e,t){const r={detailTargets:[],intervalTime:void 0,onBeforeSaveFile:void 0,onCrawlItemComplete:void 0};let o={targets:[]};if(v(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;o=t,r.detailTargets.push(...F(e))}else r.detailTargets.push(...T(t)?t:[t]);A(e,o,r);const a=!w(o?.storeDir),n=!w(o?.extension);return r.detailTargets.forEach((e=>{w(e.storeDir)&&a&&(e.storeDir=o.storeDir),w(e.extension)&&n&&(e.extension=o.extension)})),r.onBeforeSaveFile=o.onBeforeSaveFile,r}(e,t);p(o);const s={saveFileErrorArr:[],saveFilePendingQueue:[],intervalTime:a,onCrawlItemComplete:i,onBeforeSaveFile:n},l=await R("file",e.mode,o,s,q,M),{saveFilePendingQueue:c,saveFileErrorArr:u}=s;var d;await Promise.all(c),(d=u,function e(t,r){if(t>=r)return;const o=d[r];let a=t,n=r-1;for(;a<=n;){for(;d[a]o;)n--;a<=n&&(b(d,a,n),a++,n--)}b(d,a,r),e(t,a-1),e(a+1,r)}(0,d.length-1),d).forEach((e=>p(g(e.message))));const m=[],f=[];l.forEach((e=>{e.data?.data.isSuccess?m.push(e.id):f.push(e.id)})),p("Save file final result:"),p(h(` Success - total: ${m.length}, ids: [ ${m.join(" - ")} ]`)),p(g(` Error - total: ${f.length}, ids: [ ${f.join(" - ")} ]`));const y=T(t)||v(t)&&Object.hasOwn(t,"targets")?l:l[0];return r&&r(y),y}}function N(e,t){const{d:r,h:o,m:a}=e,n=(w(r)?0:1e3*r*60*60*24)+(w(o)?0:1e3*o*60*60)+(w(a)?0:1e3*a*60);let i=0;l();const s=setInterval(l,n);function l(){console.log(h(`Start the ${f.bold(++i)} polling`)),t(i,c)}function c(){clearInterval(s),console.log(h("Stop the polling"))}}const H=function(e){const t=function(e){const t=e||{};return w(t.mode)&&(t.mode="async"),w(t.enableRandomFingerprint)&&(t.enableRandomFingerprint=!0),w(e?.timeout)&&(t.timeout=1e4),w(e?.maxRetry)&&(t.maxRetry=0),t}(e);return function(e){return{crawlPage:D(e),crawlData:L(e),crawlFile:W(e),startPolling:N}}(t)}({enableRandomFingerprint:!1,baseUrl:"https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area",intervalTime:{max:5e3,min:3e3}});H.crawlFile({targets:["/4401.jpg","/4403.jpg","/4404.jpg","/4406.jpg","/4407.jpg"],proxy:"http://localhost:14892",headers:{test:"test"},storeDir:u.resolve(__dirname,"./upload"),onBeforeSaveFile:e=>c(e.data).resize(200).toBuffer(),onCrawlItemComplete(e){}}).then((async e=>{e.forEach((e=>{console.log(e.data?.data.isSuccess)}))})); +"use strict";var e=require("node:fs"),t=require("node:fs/promises"),r=require("node:path"),n=require("puppeteer"),o=require("chalk"),a=require("node:http"),i=require("node:https"),s=require("node:url"),l=require("https-proxy-agent");function c(e,t=0){let r=Math.floor(Math.random()*e);for(;r1){const e=t?r:c(r.max,r.min);u(`Target id: ${h(n)} - Sleep time: ${h(e+"ms")}`),await function(e){return new Promise((t=>setTimeout(t,e)))}(e)}else u(`Target id: ${h(n)} - Sleep time: ${h("0ms")}`)}async function C(e,t,r){const{intervalTime:n}=t,o=!y(n),a=w(n),i=[];for(const s of e){const{id:e}=s;await T(o,a,n,e),i.push(r(s,t))}await Promise.all(i)}async function S(e,t,r){const{intervalTime:n}=t,o=!y(n),a=w(n);for(const i of e){const{id:e}=i;await T(o,a,n,e),await r(i,t)}}function b(e,t,r){const n=e[t];e[t]=e[r],e[r]=n}function $(e){if(1===e.length)return e;const t=Math.floor(e.length/2),r=$(e.slice(0,t)),n=$(e.slice(t)),o=[];let a=0,i=0;for(;a=n[i]?(o.push(r[a]),a++):(o.push(n[i]),i++);return ae.priority===t[0].priority))?$(t.map((e=>({...e,valueOf:()=>e.priority})))):t).map(((e,t)=>{const r=++t,{maxRetry:n,proxyDetails:o}=e,a=[];return{id:r,isHandle:!1,isSuccess:!1,isStatusNormal:!1,detailTargetConfig:e,detailTargetResult:null,maxRetry:n,retryCount:0,proxyDetails:o,crawlErrorQueue:a,result:{id:r,isSuccess:!1,maxRetry:n,retryCount:0,proxyDetails:o,crawlErrorQueue:a,data:null}}}));u(m(`Start crawling - type: ${o}, mode: ${e}, total: ${a.length}`));const i="async"===e?C:S;let s=0,l=a;for(;l.length;)if(await i(l,r,n),l=l.filter((e=>{const{isHandle:t,retryCount:r,maxRetry:n,detailTargetConfig:o,proxyDetails:a,crawlErrorQueue:i,isStatusNormal:s}=e;let l=!1;if(!t&&r=2)){const e=o.proxy?.switchByErrorCount;if(!s||!y(e)&&e>=i.length){a.find((e=>e.url===o.proxyUrl)).state=!1;const e=a.find((e=>e.state))?.url;y(e)||(o.proxyUrl=e)}}return l})),l.length){const e=l.map((e=>(e.retryCount++,e.id)));u(g(`Start retrying - count: ${++s}, targets id: [ ${e.join(", ")} ]`))}const c=[],h=[];return a.forEach((e=>{e.isSuccess?c.push(e.id):h.push(e.id)})),u(p(`Crawl ${o}s finish:`)),u(d(` Success - total: ${c.length}, targets id: [ ${c.join(", ")} ]`)),u(f(` Error - total: ${h.length}, targets id: [ ${h.join(", ")} ]`)),a.map((e=>e.result))}function M(e,t){let r=e?`${e}`:"?";if(t)for(const e in t){r+=`&${e}=${t[e]}`}else r=e;return r}function R(e){const{protocol:t,hostname:r,port:n,pathname:o,search:c}=new s.URL(e.url),u="http:"===t,m={agent:e.proxyUrl?l(e.proxyUrl):u?new a.Agent:new i.Agent,protocol:t,hostname:r,port:n,path:o,search:M(c,e.params),method:e.method?.toLocaleUpperCase()??"GET",headers:{},timeout:e.timeout};return m.headers=function(e,t){const r={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",...e.headers??{}};return"POST"===t.method&&e.data&&(r["Content-Type"]="application/json",r["Content-Length"]=Buffer.byteLength(e.data)),r}(e,m),m}const E=[{platform:"Windows",mobile:"random",userAgent:{value:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",versions:[{name:"Chrome",maxMajorVersion:112,minMajorVersion:100,maxMinorVersion:10,maxPatchVersion:5615},{name:"Safari",maxMinorVersion:36,maxPatchVersion:2333}]}},{platform:"Windows",mobile:"random",userAgent:{value:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59",versions:[{name:"Chrome",maxMajorVersion:91,minMajorVersion:88,maxMinorVersion:10,maxPatchVersion:5615},{name:"Safari",maxMinorVersion:36,maxPatchVersion:2333},{name:"Edg",maxMinorVersion:10,maxPatchVersion:864}]}},{platform:"Windows",mobile:"random",userAgent:{value:"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0",versions:[{name:"Firefox",maxMajorVersion:47,minMajorVersion:43,maxMinorVersion:10,maxPatchVersion:5e3}]}}];function V(e){return v(e)?e.map((e=>x(e)?e:{url:e})):[x(e)?e:{url:e}]}function F(e,t){const{ua:r,platform:n,platformVersion:o,mobile:a,acceptLanguage:i,userAgent:s}=t;let l=e.headers;if(l||(e.headers=l={}),r&&(l["sec-ch-ua"]=r),a&&(l["sec-ch-ua-mobile"]="random"===a?c(2)?"?1":"?0":a),n&&(l["sec-ch-platform"]=n),o&&(l["sec-ch-ua-platform-version"]=o),i&&(l["accept-language"]=i),s){let e=s.value;s.versions?.forEach((t=>{const{name:r,maxMajorVersion:n,minMajorVersion:o,maxMinorVersion:a,minMinorVersion:i,maxPatchVersion:s,minPatchVersion:l}=t,u=e.split(`${r}/`)[1].split(" ")[0].split("."),m=u.join(".");y(n)||(u[0]=n===o?n:c(n,o)),y(a)||(u[1]=a===i?a:c(a,i)),y(s)||(u[2]=s===l?s:c(s,l));const p=`${r}/${m}`,d=`${r}/${u.join(".")}`;e=e.replace(p,d)})),l["user-agent"]=e}}function O(e,t){const{maxWidth:r,minWidth:n,maxHeight:o,minHidth:a}=t,i=e.viewport??{};r&&(i.width=r===n?r:c(r,n)),o&&(i.height=o===a?o:c(o,a)),Object.hasOwn(i,"width")&&Object.hasOwn(i,"height")&&(e.viewport=i)}function P(e,t,r){r.detailTargets=t.detailTargets.map((n=>{const o=n,{url:a,timeout:i,proxy:s,maxRetry:l,priority:u,headers:m,fingerprint:p}=o;if(y(e.baseUrl)||(o.url=e.baseUrl+a),y(i)&&(y(t.timeout)?o.timeout=e.timeout:o.timeout=t.timeout),y(l)&&(y(t.maxRetry)?o.maxRetry=e.maxRetry:o.maxRetry=t.maxRetry),y(s)&&(y(t.proxy)?y(e.proxy)||(o.proxy=e.proxy):o.proxy=t.proxy),y(o.proxy?.urls))o.proxyDetails=[];else{const e=o.proxy.urls;o.proxyUrl=e[0],o.proxyDetails=e.map((e=>({url:e,state:!0})))}if(y(u)&&(o.priority=0),y(m)&&t.headers&&(o.headers={...t.headers}),p)F(o,p);else if(y(p)&&v(t.fingerprints)&&t.fingerprints.length){const e=t.fingerprints,n=c(e.length),a=e[n];r.selectFingerprintIndexs.push(n),F(o,a)}else if(y(p)&&!v(t.fingerprints)&&e.enableRandomFingerprint){F(o,E[c(E.length)])}return o})),r.intervalTime=t.intervalTime,y(t.intervalTime)&&!y(e.intervalTime)&&(r.intervalTime=e.intervalTime),r.onCrawlItemComplete=t.onCrawlItemComplete}async function k(e,t){const{detailTargetConfig:r,detailTargetResult:n,retryCount:o,maxRetry:a,crawlErrorQueue:i}=e,{browser:s}=t,l=o===a,c=n?.page??await s.newPage();r.viewport&&await c.setViewport(r.viewport);let u=null,m=!0;try{if(r.proxyUrl?await s.createIncognitoBrowserContext({proxyServer:r.proxyUrl}):await s.createIncognitoBrowserContext({proxyServer:void 0}),r.cookies){const e=function(e,t){const r=[];return"string"==typeof t?t.split("; ").forEach((t=>{const n=t.split("=");r.push({name:n[0],value:n[1],url:e})})):Array.isArray(t)?t.forEach((t=>{t.url||(t.url=e),r.push(t)})):"object"==typeof t&&t&&(t.url||(t.url=e),r.push(t)),r}(r.url,r.cookies);await c.setCookie(...e)}else{const e=await c.cookies(r.url);await c.deleteCookie(...e)}r.headers&&await c.setExtraHTTPHeaders(r.headers),u=await c.goto(r.url,{timeout:r.timeout})}catch(e){m=!1,i.push(e)}e.detailTargetResult={response:u,page:c};const p=!j(e),d=m&&p;e.isStatusNormal=p,e.isSuccess=d,(d||l)&&(e.isHandle=!0,function(e,t){const{detailTargetResult:r,result:n}=e,{browser:o,onCrawlItemComplete:a}=t;W(e),n.data={browser:o,...r},a&&a(e.result)}(e,t))}async function A(n,o){const{detailTargetConfig:s,crawlErrorQueue:l,maxRetry:c,retryCount:u}=n,m=c===u;let p=null,d=!0;try{p=await(f=s,new Promise(((e,t)=>{const r=y(f.data);f.data=r?f.data:JSON.stringify(f.data);const n=R(f);function o(t){const{statusCode:r,headers:n}=t,o=[];t.on("data",(e=>o.push(e))),t.on("end",(()=>{const t=Buffer.concat(o);e({statusCode:r,headers:n,data:t})}))}let s;s="http:"===n.protocol?a.request(n,o):i.request(n,o),s.on("timeout",(()=>{t(new Error(`Timeout ${f.timeout}ms`))})),s.on("error",(e=>{t(e)})),"POST"!==n.method||r||s.write(f.data),s.end()})))}catch(e){d=!1,l.push(e)}var f;n.detailTargetResult=p;const g=!j(n),h=d&&g;n.isStatusNormal=g,n.isSuccess=h,(h||m)&&(n.isHandle=!0,"data"===o.type?function(e,t){const{isSuccess:r,detailTargetResult:n,result:o}=e,{onCrawlItemComplete:a}=t;if(W(e),r&&n){const e=n.headers["content-type"]??"",t="application/json"===e?JSON.parse(n.data.toString()):e.includes("text")?n.data.toString():n.data;o.data={...n,data:t}}a&&a(o)}(n,o):"file"===o.type&&function(n,o){const{id:a,isSuccess:i,detailTargetConfig:s,detailTargetResult:l,result:c}=n,{saveFileErrorArr:u,saveFilePendingQueue:m,onCrawlItemComplete:p,onBeforeSaveItemFile:d}=o;if(W(n),i&&l){const o=l.headers["content-type"]??"",i=s.fileName??`${a}-${(new Date).getTime()}`,f=s.extension??`.${o.split("/").pop()}`;s.storeDir&&!e.existsSync(s.storeDir)&&e.mkdirSync(s.storeDir,{recursive:!0});const g=s.storeDir??__dirname,h=r.resolve(g,i+f),y=l.data;let w=Promise.resolve(y);d&&(w=d({id:a,fileName:i,filePath:h,data:y}));const x=w.then((async e=>{let r=!0;try{await t.writeFile(h,e)}catch(e){r=!1;const t=`File save error at id ${a}: ${e.message}`,n=()=>a;u.push({message:t,valueOf:n})}const s=e.length;c.data={...l,data:{isSuccess:r,fileName:i,fileExtension:f,mimeType:o,size:s,filePath:h}},p&&p(n.result)}));m.push(x)}else p&&p(n.result)}(n,o))}const D=["isSuccess","retryCount"];function W(e){Object.keys(e).forEach((t=>{D.includes(t)&&(e.result[t]=e[t])}))}function B(e){let t=null,r=null,o=!1;return async function(a,i){o||(o=!0,r=n.launch(e.crawlPage?.launchBrowser).then((e=>{t=e}))),r&&(await r,r&&(r=null));const{detailTargets:s,intervalTime:l,onCrawlItemComplete:c}=function(e,t){const r={detailTargets:[],intervalTime:void 0,selectFingerprintIndexs:[],onCrawlItemComplete:void 0};let n={targets:[],detailTargets:[]};if(x(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;n=t,n.detailTargets=V(e)}else n.detailTargets=V(t);return P(e,n,r),r.detailTargets.forEach(((e,t)=>{const{cookies:o,viewport:a,fingerprint:i}=e;if(y(o)&&n.cookies&&(e.cookies=n.cookies),y(a)&&n.viewport&&(e.viewport=n.viewport),i)O(e,i);else if(y(i)&&n.fingerprints?.length){const o=r.selectFingerprintIndexs[t];O(e,n.fingerprints[o])}})),r}(e,a),u={type:"page",browser:t,intervalTime:l,onCrawlItemComplete:c},m=await I(e.mode,s,u,k),p=v(a)||x(a)&&Object.hasOwn(a,"targets")?m:m[0];return i&&i(p),p}}function N(e){return async function(t,r){const{detailTargets:n,intervalTime:o,onCrawlItemComplete:a}=function(e,t){const r={detailTargets:[],intervalTime:void 0,selectFingerprintIndexs:[],onCrawlItemComplete:void 0};let n={targets:[],detailTargets:[]};if(x(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;n=t,n.detailTargets=V(e)}else n.detailTargets=V(t);return P(e,n,r),r}(e,t),i={type:"data",intervalTime:o,onCrawlItemComplete:a},s=await I(e.mode,n,i,A),l=v(t)||x(t)&&Object.hasOwn(t,"targets")?s:s[0];return r&&r(l),l}}function H(e){return async function(t,r){const{detailTargets:n,intervalTime:o,onBeforeSaveItemFile:a,onCrawlItemComplete:i}=function(e,t){const r={detailTargets:[],intervalTime:void 0,selectFingerprintIndexs:[],onBeforeSaveItemFile:void 0,onCrawlItemComplete:void 0};let n={targets:[],detailTargets:[]};if(x(t)&&Object.hasOwn(t,"targets")){const{targets:e}=t;n=t,n.detailTargets=V(e)}else n.detailTargets=v(t)?t:[t];P(e,n,r);const o=!y(n?.storeDir),a=!y(n?.extension);return r.detailTargets.forEach((e=>{y(e.storeDir)&&o&&(e.storeDir=n.storeDir),y(e.extension)&&a&&(e.extension=n.extension)})),r.onBeforeSaveItemFile=n.onBeforeSaveItemFile,r}(e,t),s={type:"file",saveFileErrorArr:[],saveFilePendingQueue:[],intervalTime:o,onCrawlItemComplete:i,onBeforeSaveItemFile:a},l=await I(e.mode,n,s,A),{saveFilePendingQueue:c,saveFileErrorArr:m}=s;var g;await Promise.all(c),(g=m,function e(t,r){if(t>=r)return;const n=g[r];let o=t,a=r-1;for(;o<=a;){for(;g[o]n;)a--;o<=a&&(b(g,o,a),o++,a--)}b(g,o,r),e(t,o-1),e(o+1,r)}(0,g.length-1),g).forEach((e=>u(f(e.message))));const h=[],w=[];l.forEach((e=>{e.data?.data.isSuccess?h.push(e.id):w.push(e.id)})),u(p("Save files finish:")),u(d(` Success - total: ${h.length}, targets id: [ ${h.join(", ")} ]`)),u(f(` Error - total: ${w.length}, targets id: [ ${w.join(", ")} ]`));const T=v(t)||x(t)&&Object.hasOwn(t,"targets")?l:l[0];return r&&r(T),T}}function q(e,t){const{d:r,h:n,m:o}=e,a=(y(r)?0:1e3*r*60*60*24)+(y(n)?0:1e3*n*60*60)+(y(o)?0:1e3*o*60);let i=0;l();const s=setInterval(l,a);function l(){console.log(m("Start polling - count: "+ ++i)),t(i,c)}function c(){clearInterval(s),console.log(g("Stop the polling"))}}const U=function(e){const t=function(e){const t=e||{};return y(t.mode)&&(t.mode="async"),y(t.enableRandomFingerprint)&&(t.enableRandomFingerprint=!0),y(e?.timeout)&&(t.timeout=1e4),y(e?.maxRetry)&&(t.maxRetry=0),t}(e);return function(e){return{crawlPage:B(e),crawlData:N(e),crawlFile:H(e),startPolling:q}}(t)}({maxRetry:2});U.crawlData(["https://","https://","https://"]).then((e=>{console.log(e)})); diff --git a/test/start/index.ts b/test/start/index.ts index e4d0c58a..0b2b6855 100644 --- a/test/start/index.ts +++ b/test/start/index.ts @@ -2,71 +2,8 @@ import xCrawl from 'x-crawl' import sharp from 'sharp' import path from 'path' -const testXCrawl = xCrawl({ - enableRandomFingerprint: false, - baseUrl: - 'https://raw.githubusercontent.com/coder-hxl/airbnb-upload/master/area', - intervalTime: { max: 5000, min: 3000 } -}) - -// testXCrawl -// .crawlFile({ -// targets: ['/4401.jpg', '/4403.jpg', '/4404.jpg', '/4406.jpg', '/4407.jpg'], -// proxy: 'http://localhost:14892', -// headers: { -// test: 'test' -// }, -// storeDir: path.resolve(__dirname, './upload'), -// onBeforeSaveItemFile(info) { -// return sharp(info.data).resize(200).toBuffer() -// }, -// onCrawlItemComplete(crawlFileSingleRes) { -// // console.log(111, crawlFileSingleRes) -// } -// }) -// .then(async (res) => { -// // console.log(res) +const testXCrawl = xCrawl({ maxRetry: 2 }) -// res.forEach((item) => { -// console.log(item.data?.data.isSuccess) -// }) -// }) - -testXCrawl - .crawlPage({ - targets: [ - 'https://github.com/coder-hxl', - { - url: 'https://github.com/coder-hxl/x-crawl', - fingerprint: null - }, - { - url: 'https://github.com/coder-hxl/x-crawl/stargazers', - fingerprint: { - maxWidth: 1980, - minWidth: 1980, - maxHeight: 1080, - minHidth: 1080, - platform: 'Android' - } - } - ], - fingerprint: { - maxWidth: 1980, - maxHeight: 1080, - userAgents: [ - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36', - 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', - 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1' - ], - platforms: ['Chromium OS', 'iOS', 'Linux', 'macOS', 'Windows'] - } - }) - .then((res) => { - res.forEach((item, i) => { - item.data.page.screenshot({ path: `./img${i}.jpg` }).then(() => { - console.log(i, 'success') - }) - }) - }) +testXCrawl.crawlData(['https://', 'https://', 'https://']).then((res) => { + console.log(res) +}) diff --git a/tsconfig.json b/tsconfig.json index aa75f766..88b8822d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,12 @@ "skipLibCheck": true, "composite": true, "outDir": "./publish", - "emitDeclarationOnly": false + "emitDeclarationOnly": false, + "baseUrl": ".", + "paths": { + "src/*": ["./src"], + "publish/*": ["./src"] + } }, "include": ["src/**/*", "test/**/*", "publish/dist/index.d.ts"] }