From cb37b235074b349ce228c312f26c9c8b1fe36e7c Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sat, 20 Jul 2024 19:17:31 +0700 Subject: [PATCH 1/8] initial deno rework --- .editorconfig | 15 + .eslintignore | 2 - .eslintrc.json | 23 - .gitignore | 8 +- .prettierignore | 3 - .prettierrc | 14 - README.md | 28 +- __tests__/client/extraction.test.ts | 172 +- __tests__/client/init.test.ts | 31 +- __tests__/client/scrape.test.ts | 796 +-- __tests__/client/screenshot.test.ts | 160 +- __tests__/config/extraction.test.ts | 250 +- __tests__/config/scrape.test.ts | 979 ++-- __tests__/config/screenshot.test.ts | 388 +- __tests__/result.test.ts | 44 +- __tests__/utils.test.ts | 122 +- build.ts | 66 + deno.json | 30 + jest.config.js | 14 - package-lock.json | 8074 --------------------------- package.json | 62 - publish-jsr.sh | 22 + src/client.ts | 688 ++- src/deps.ts | 4 + src/errors.ts | 10 +- src/extractionconfig.ts | 205 +- src/logger.ts | 66 +- src/main.ts | 29 +- src/result.ts | 557 +- src/scrapeconfig.ts | 618 +- src/screenshotconfig.ts | 268 +- src/utils.ts | 51 +- tsconfig.json | 4 +- 33 files changed, 3084 insertions(+), 10719 deletions(-) create mode 100644 .editorconfig delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json delete mode 100644 .prettierignore delete mode 100644 .prettierrc create mode 100644 build.ts create mode 100644 deno.json delete mode 100644 jest.config.js delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100755 publish-jsr.sh create mode 100644 src/deps.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f4f6be7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false + +[*.{js,json,ts,mts,yml,yaml}] +indent_size = 2 +indent_style = space diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f12e5cb..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -/**/*.js -build/ diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 7b96b67..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "env": { - "browser": false, - "es6": true, - "node": true - }, - "ignorePatterns": ["**/*.d.ts", "examples/**/*"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "tsconfig.json", - "sourceType": "module", - "ecmaVersion": 2020 - }, - "plugins": ["@typescript-eslint", "jest"], - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:jest/recommended", "prettier"], - "rules": { - // The following rule is enabled only to supplement the inline suppression - // examples, and because it is not a recommended rule, you should either - // disable it, or understand what it enforces. - // https://typescript-eslint.io/rules/explicit-function-return-type/ - "@typescript-eslint/explicit-function-return-type": "warn" - } -} diff --git a/.gitignore b/.gitignore index 3091757..ebe64e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ node_modules -coverage \ No newline at end of file +coverage +scripts +npm +dev +cov_profile +.vscode +.history diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index fe2cb3b..0000000 --- a/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -/.history/ -/examples/.history/ -/.github/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 2d57920..0000000 --- a/.prettierrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "printWidth": 120, - "tabWidth": 4, - "overrides": [ - { - "files": ["*.ts", "*.mts"], - "options": { - "parser": "typescript" - } - } - ] -} diff --git a/README.md b/README.md index d353dfc..ef8540d 100644 --- a/README.md +++ b/README.md @@ -70,15 +70,19 @@ new ScrapeConfig({ ## Development -Install and setup environment: - -```shell -$ npm install -``` - -Build and test: - -```shell -$ npm run build -$ npm run test -``` +This is a Deno Typescript project that builds to NPM through [DNT](https://github.com/denoland/dnt). + +- `/src` directory contains all of the source code with `main.ts` being the entry point. +- `__tests__` directory contains tests for the source code. +- `deno.json` contains meta information +- `build.ts` is the build script that builds the project to nodejs ESM package. +- `/npm` directory will be produced when `buil.ts` is executed for building node package. + +```bash +# make modifications and run tests +$ deno task test +# format +$ deno fmt +# lint +$ deno linst +``` \ No newline at end of file diff --git a/__tests__/client/extraction.test.ts b/__tests__/client/extraction.test.ts index f1b1a23..5b7717d 100644 --- a/__tests__/client/extraction.test.ts +++ b/__tests__/client/extraction.test.ts @@ -1,99 +1,111 @@ -import * as errors from '../../src/errors.js'; -import { ScrapflyClient } from '../../src/client.js'; -import { ExtractionConfig } from '../../src/extractionconfig.js' -import { describe, it, expect, beforeEach, jest } from '@jest/globals'; -import { responseFactory } from '../utils.js'; +import * as errors from '../../src/errors.ts'; +import { ScrapflyClient } from '../../src/client.ts'; +import { ExtractionConfig } from '../../src/extractionconfig.ts'; +import { assertEquals, assertRejects } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { stub } from "https://deno.land/std/testing/mock.ts"; +import { responseFactory } from '../utils.ts'; -describe('extract', () => { +Deno.test('extract: succeeds', async () => { const KEY = '__API_KEY__'; const client = new ScrapflyClient({ key: KEY }); - - beforeEach(() => { - jest.spyOn(client, 'fetch').mockClear(); // clear all mock meta on each test + const html = 'very long html file'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const configUrl = new URL(config.url); + const configBody = await new Response(config.body).text(); + assertEquals(configUrl.origin + configUrl.pathname, client.HOST + '/extraction'); + assertEquals(config.method, 'POST'); + assertEquals(configUrl.searchParams.get('key'), KEY); + assertEquals(configBody, html); + const body = { data: 'a document summary', content_type: 'text/html' }; + return responseFactory(body, { status: 200 }); }); - it('succeeds', async () => { - const spy = jest.spyOn(client, 'fetch'); - const html = 'very long html file'; - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const configUrl = config[Object.getOwnPropertySymbols(config)[1]].url; - const configBody = config[Object.getOwnPropertySymbols(config)[1]].body.source; - // Ensure the URL matches the pattern - expect(configUrl.origin + configUrl.pathname).toEqual(client.HOST + '/extraction'); - expect(config.method).toEqual('POST'); - expect(configUrl.searchParams.get('key')).toMatch(KEY); - expect(configBody).toEqual(html); - const body = { data: 'a document summary', content_type: 'text/html' }; - return responseFactory(body, { - status: 200, - }); - }); + const result = await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' })); + assertEquals(result.content_type, 'text/html'); + assertEquals(result.data, 'a document summary'); + assertEquals(fetchStub.calls.length, 1); - const result = await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' })); - expect(result).toBeDefined(); - expect(result.content_type).toBe('text/html'); - expect(result.data).toBe('a document summary'); - expect(spy).toHaveBeenCalledTimes(1); - }); + fetchStub.restore(); +}); - it('fails due to failing to invalid config', async () => { - const html = 'very long html file'; - await expect( - client.extract( +Deno.test('extract: fails due to invalid config', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + const html = 'very long html file'; + await assertRejects( + async () => { + await client.extract( new ExtractionConfig({ body: html, content_type: 'text/html', ephemeral_template: { source: 'html' }, template: 'template', }), - ), - ).rejects.toThrow(errors.ExtractionConfigError); - }); + ); + }, + errors.ExtractionConfigError + ); +}); - it('fails to invalid API key', async () => { - const html = 'very long html file'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const result = { - status: 'error', - http_code: 401, - reason: 'Unauthorized', - error_id: '301e2d9e-b4f5-4289-85ea-e452143338df', - message: 'Invalid API key', - }; - return responseFactory(result, { - status: 401, - headers: { - 'Content-Type': 'application/json', - }, - }); +Deno.test('extract: fails due to invalid API key', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + const html = 'very long html file'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = { + status: 'error', + http_code: 401, + reason: 'Unauthorized', + error_id: '301e2d9e-b4f5-4289-85ea-e452143338df', + message: 'Invalid API key', + }; + return responseFactory(result, { + status: 401, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }))).rejects.toThrow( - errors.BadApiKeyError, - ); }); - it('fails to any extraction related error', async () => { - const html = 'very long html file'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const result = { - code: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', - error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494', - http_code: 422, - links: { - 'Related Error Doc': - 'https://scrapfly.io/docs/extraction-api/error/ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', - }, - message: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', - }; - return responseFactory(result, { - status: 422, - headers: { - 'Content-Type': 'application/json', - }, - }); + await assertRejects( + async () => { + await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' })); + }, + errors.BadApiKeyError + ); + + fetchStub.restore(); +}); + +Deno.test('extract: fails due to any extraction related error', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + const html = 'very long html file'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = { + code: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', + error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494', + http_code: 422, + links: { + 'Related Error Doc': + 'https://scrapfly.io/docs/extraction-api/error/ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', + }, + message: 'ERR::EXTRACTION::CONTENT_TYPE_NOT_SUPPORTED', + }; + return responseFactory(result, { + status: 422, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' }))).rejects.toThrow( - errors.ExtractionApiError, - ); }); -}); \ No newline at end of file + + await assertRejects( + async () => { + await client.extract(new ExtractionConfig({ body: html, content_type: 'text/html' })); + }, + errors.ExtractionApiError + ); + + fetchStub.restore(); +}); diff --git a/__tests__/client/init.test.ts b/__tests__/client/init.test.ts index 574c9ff..a0e6c6c 100644 --- a/__tests__/client/init.test.ts +++ b/__tests__/client/init.test.ts @@ -1,17 +1,18 @@ -import * as errors from '../../src/errors.js'; -import { ScrapflyClient } from '../../src/client.js'; +import * as errors from '../../src/errors.ts'; +import { ScrapflyClient } from '../../src/client.ts'; +import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts"; -import { describe, it, expect } from '@jest/globals'; - -describe('client init', () => { - it('success', async () => { - const client = new ScrapflyClient({ key: '1234' }); - expect(client).toBeDefined(); - }); - - it('invalid key', async () => { - expect(() => { - new ScrapflyClient({ key: null }); - }).toThrow(errors.BadApiKeyError); - }); +Deno.test('client init: success', () => { + const client = new ScrapflyClient({ key: '1234' }); + assertEquals(!!client, true, "client should be defined"); }); + +Deno.test('client init: invalid key', () => { + assertThrows( + () => { + new ScrapflyClient({ key: "" }); + }, + errors.BadApiKeyError, + "Invalid key. Key must be a non-empty string" + ); +}); \ No newline at end of file diff --git a/__tests__/client/scrape.test.ts b/__tests__/client/scrape.test.ts index 0f3ef4a..3e4ecea 100644 --- a/__tests__/client/scrape.test.ts +++ b/__tests__/client/scrape.test.ts @@ -1,254 +1,395 @@ -import * as errors from '../../src/errors.js'; -import { ScrapflyClient } from '../../src/client.js'; -import { ScrapeConfig } from '../../src/scrapeconfig.js'; -import { describe, it, expect, beforeEach, jest } from '@jest/globals'; -import { resultFactory, responseFactory } from '../utils.js'; +import * as errors from '../../src/errors.ts'; +import { ScrapflyClient } from '../../src/client.ts'; +import { ScrapeConfig } from '../../src/scrapeconfig.ts'; +import { log } from '../../src/logger.ts'; +import { assertEquals, assertRejects } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { resultFactory, responseFactory } from '../utils.ts'; +import { stub } from "https://deno.land/std/testing/mock.ts"; -describe('scrape', () => { +log.setLevel('DEBUG'); + +Deno.test('scrape: GET success', async () => { const KEY = '__API_KEY__'; const client = new ScrapflyClient({ key: KEY }); - beforeEach(() => { - jest.spyOn(client, 'fetch').mockClear(); // clear all mock meta on each test + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const configUrl = new URL(config.url); + assertEquals(configUrl.origin + configUrl.pathname, client.HOST + '/scrape'); + assertEquals(config.method, 'GET'); + assertEquals(configUrl.searchParams.get('key'), KEY); + assertEquals(configUrl.searchParams.get('url'), 'https://httpbin.dev/json'); + assertEquals(Array.from((configUrl.searchParams as any).keys()), ['key', 'url']); + const result = resultFactory({ + url: 'https://httpbin.dev/json', + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); }); - it('GET success', async () => { - const spy = jest.spyOn(client, 'fetch'); - const url = 'https://httpbin.dev/json'; - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const configUrl = config[Object.getOwnPropertySymbols(config)[1]].url; - // Ensure the URL matches the pattern - expect(configUrl.origin + configUrl.pathname).toEqual(client.HOST + '/scrape'); - expect(config.method).toEqual('GET'); - expect(configUrl.searchParams.get('key')).toMatch(KEY); - expect(configUrl.searchParams.get('url')).toMatch(url); - expect(Array.from(configUrl.searchParams.keys())).toEqual(['key', 'url']); - const result = resultFactory({ - url: url, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); - }); + const result = await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + assertEquals(result !== undefined, true); + + assertEquals(fetchStub.calls.length, 1); - const result = await client.scrape(new ScrapeConfig({ url: url })); - expect(result).toBeDefined(); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ApiHttpServerError on 500 and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); - // a single request - expect(spy).toHaveBeenCalledTimes(1); + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: 'https://httpbin.dev/json', + status_code: 500, + status: 'ERROR', + success: true, + error: 'failed to connect to upstream server', + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ApiHttpServerError, + ); + + fetchStub.restore(); }); -describe('scrape errors', () => { +Deno.test('scrape errors: raises BadApiKeyError on 401', async () => { const KEY = '__API_KEY__'; const client = new ScrapflyClient({ key: KEY }); - beforeEach(() => { - jest.spyOn(client, 'fetch').mockClear(); + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = { + status: 'error', + http_code: 401, + reason: 'Unauthorized', + error_id: '301e2d9e-b4f5-4289-85ea-e452143338df', + message: 'Invalid API key', + }; + return responseFactory(result, { + status: 401, + headers: { + 'Content-Type': 'application/json', + }, + }); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.BadApiKeyError, + ); - it('raises ApiHttpServerError on 500 and success', async () => { - const url = 'https://httpbin.dev/json'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const result = resultFactory({ - url: url, - status_code: 500, - status: 'ERROR', - success: true, - error: 'failed to connect to upstream server', - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises TooManyRequests on 429 and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: 'https://httpbin.dev/json', + status_code: 429, + status: 'ERROR', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url }))).rejects.toThrow(errors.ApiHttpServerError); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.TooManyRequests, + ); - it('raises BadApiKeyError on 401', async () => { - const url = 'https://httpbin.dev/json'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const result = { - status: 'error', - http_code: 401, - reason: 'Unauthorized', - error_id: '301e2d9e-b4f5-4289-85ea-e452143338df', - message: 'Invalid API key', - }; - return responseFactory(result, { - status: 401, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflyScrapeError on ::SCRAPE:: resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::SCRAPE::BAD_PROTOCOL', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url }))).rejects.toThrow(errors.BadApiKeyError); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyScrapeError, + ); - it('raises TooManyRequests on 429 and success', async () => { - const url = 'https://httpbin.dev/json'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const result = resultFactory({ - url: url, - status_code: 429, - status: 'ERROR', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflyWebhookError on ::WEBHOOK:: resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::WEBHOOK::DISABLED ', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url }))).rejects.toThrow(errors.TooManyRequests); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyWebhookError, + ); - it('raises ScrapflyScrapeError on ::SCRAPE:: resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::SCRAPE::BAD_PROTOCOL', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflyProxyError on ERR::PROXY::POOL_NOT_FOUND resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::PROXY::POOL_NOT_FOUND ', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyScrapeError, - ); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyProxyError, + ); - it('raises ScrapflyWebhookError on ::WEBHOOK:: resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::WEBHOOK::DISABLED ', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflyScheduleError on ERR::SCHEDULE::DISABLED resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::SCHEDULE::DISABLED', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyWebhookError, - ); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyScheduleError, + ); - it('raises ScrapflyProxyError on ERR::PROXY::POOL_NOT_FOUND resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::PROXY::POOL_NOT_FOUND ', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflyAspError on ERR::ASP::SHIELD_ERROR resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::ASP::SHIELD_ERROR', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyProxyError, - ); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyAspError, + ); - it('raises ScrapflyScheduleError on ERR::SCHEDULE::DISABLED resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::SCHEDULE::DISABLED', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ScrapflySessionError on ERR::SESSION::CONCURRENT_ACCESS resource and success', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::SESSION::CONCURRENT_ACCESS', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyScheduleError, - ); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflySessionError, + ); - it('raises ScrapflyAspError on ERR::ASP::SHIELD_ERROR resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::ASP::SHIELD_ERROR', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises ApiHttpClientError on success and unknown status', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + status: 'ERR::NEW', + success: true, + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyAspError, - ); }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ApiHttpClientError, + ); - it('raises ScrapflySessionError on ERR::SESSION::CONCURRENT_ACCESS resource and success', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::SESSION::CONCURRENT_ACCESS', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises UpstreamHttpServerError on failure, ERR::SCRAPE::BAD_UPSTREAM_RESPONSE and >=500', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + success: false, + status_code: 500, + status: 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE', + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflySessionError, - ); }); - it('raises ApiHttpClientError on success and unknown status', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - status: 'ERR::NEW', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.UpstreamHttpServerError, + ); + + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises UpstreamHttpClientError on failure, ERR::SCRAPE::BAD_UPSTREAM_RESPONSE and 4xx status', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + success: false, + status_code: 404, + status: 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE', + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ApiHttpClientError, - ); }); - it('raises UpstreamHttpServerError on failure, ERR::SCRAPE::BAD_UPSTREAM_RESPONSE and >=500', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.UpstreamHttpClientError, + ); + + fetchStub.restore(); +}); + +Deno.test('scrape errors: raises resource exceptions on failure', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const resourceErrMap = { + SCRAPE: errors.ScrapflyScrapeError, + WEBHOOK: errors.ScrapflyWebhookError, + PROXY: errors.ScrapflyProxyError, + SCHEDULE: errors.ScrapflyScheduleError, + ASP: errors.ScrapflyAspError, + SESSION: errors.ScrapflySessionError, + }; + + for (const [resource, err] of Object.entries(resourceErrMap)) { + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { const result = resultFactory({ url: config.url, success: false, - status_code: 500, - status: 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE', + status: `ERR::${resource}::MISSING`, }); return responseFactory(result.data, { status: 200, @@ -257,18 +398,113 @@ describe('scrape errors', () => { }, }); }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.UpstreamHttpServerError, + + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + err, + ); + + fetchStub.restore(); + } +}); + +Deno.test('scrape errors: raises ScrapflyError on unhandled failure', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const result = resultFactory({ + url: config.url, + success: false, + status_code: 404, + status: 'ERR', + }); + return responseFactory(result.data, { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + await assertRejects( + async () => { + await client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' })); + }, + errors.ScrapflyError, + ); + + fetchStub.restore(); +}); + +Deno.test('scrape errors: account retrieval status unhandled code (e.g. 404)', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + return responseFactory( + {}, + { + status: 404, + }, ); }); - it('raises UpstreamHttpClientError on failure, ERR::SCRAPE::BAD_UPSTREAM_RESPONSE and 4xx status', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { + await assertRejects( + async () => { + await client.account(); + }, + errors.HttpError, + ); + + fetchStub.restore(); +}); + +Deno.test('scrape errors: account retrieval bad api key (status 401)', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + return responseFactory( + {}, + { + status: 401, + }, + ); + }); + + await assertRejects( + async () => { + await client.account(); + }, + errors.BadApiKeyError, + ); + + fetchStub.restore(); +}); + +Deno.test('concurrent scrape: success with explicit concurrency', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + + const configs = [ + ...new Array(5).fill(null).map(() => new ScrapeConfig({ url: 'https://httpbin.dev/status/200' })), + ...new Array(5).fill(null).map(() => new ScrapeConfig({ url: 'https://httpbin.dev/status/400' })), + ]; + const results = []; + const errors = []; + + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + await new Promise((resolve) => setTimeout(resolve, 100)); // XXX: NEEDS a delay! + log.error(config.url); + if (config.url.includes('200')) { const result = resultFactory({ url: config.url, - success: false, - status_code: 404, - status: 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE', + status: 'DONE', + success: true, + status_code: 200, }); return responseFactory(result.data, { status: 200, @@ -276,46 +512,11 @@ describe('scrape errors', () => { 'Content-Type': 'application/json', }, }); - }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.UpstreamHttpClientError, - ); - }); - - it('raises resource exceptions on failure', async () => { - const resourceErrMap = { - SCRAPE: errors.ScrapflyScrapeError, - WEBHOOK: errors.ScrapflyWebhookError, - PROXY: errors.ScrapflyProxyError, - SCHEDULE: errors.ScrapflyScheduleError, - ASP: errors.ScrapflyAspError, - SESSION: errors.ScrapflySessionError, - }; - for (const [resource, err] of Object.entries(resourceErrMap)) { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const result = resultFactory({ - url: config.url, - success: false, - status: `ERR::${resource}::MISSING`, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); - }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow(err); - } - }); - - it('raises ScrapflyError on unhandled failure', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { + } else { const result = resultFactory({ url: config.url, - success: false, - status_code: 404, - status: 'ERR', + status: 'ERR::ASP::SHIELD_ERROR', + success: true, }); return responseFactory(result.data, { status: 200, @@ -323,90 +524,25 @@ describe('scrape errors', () => { 'Content-Type': 'application/json', }, }); - }); - await expect(client.scrape(new ScrapeConfig({ url: 'https://httpbin.dev/json' }))).rejects.toThrow( - errors.ScrapflyError, - ); + } }); - it('account retrieval status unhandled code (e.g. 404)', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - return responseFactory( - {}, - { - status: 404, - }, - ); - }); - await expect(client.account()).rejects.toThrow(errors.HttpError); - }); - it('account retrieval bad api key (status 401)', async () => { - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - return responseFactory( - {}, - { - status: 401, - }, - ); - }); - await expect(client.account()).rejects.toThrow(errors.BadApiKeyError); - }); -}); + for await (const resultOrError of client.concurrentScrape(configs, 5)) { + if (resultOrError instanceof Error) { + errors.push(resultOrError); + } else { + results.push(resultOrError); + } + } -describe('concurrent scrape', () => { - const KEY = '__API_KEY__'; - const client = new ScrapflyClient({ key: KEY }); - // mock fetch to return /account data and 2 types of results: - // - success for /success endpoints - // - ASP failure for /failure endpoints - - it('success with explicit concurrency', async () => { - const spy = jest.spyOn(client, 'fetch'); - spy.mockImplementation(async (config: Request): Promise => { - const configs = [ - ...new Array(5).fill(null).map(() => new ScrapeConfig({ url: 'https://httpbin.dev/status/200' })), - ...new Array(5).fill(null).map(() => new ScrapeConfig({ url: 'https://httpbin.dev/status/400' })), - ]; - const results = []; - const errors = []; - if (config.url.includes('/200')) { - const result = resultFactory({ - url: config.url, - status: 'DONE', - success: true, - status_code: 200, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); - } - if (config.url.includes('/400')) { - const result = resultFactory({ - url: config.url, - status: 'ERR::ASP::SHIELD_ERROR', - success: true, - }); - return responseFactory(result.data, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); - } - for await (const resultOrError of client.concurrentScrape(configs, 10)) { - if (resultOrError instanceof Error) { - errors.push(resultOrError); - } else { - results.push(resultOrError); - } - } - expect(results.length).toBe(5); - expect(errors.length).toBe(5); - expect(spy).toHaveBeenCalledTimes(10); - }); - }, 10_000); -}); + + assertEquals(errors.length, 5, "expected 5 errors"); + assertEquals(results.length, 5, "expected 5 successes"); + assertEquals(fetchStub.calls.length, 10, "expected 10 fetch calls"); + assertEquals(configs.length, 10, "expected 10 configs to be setup"); + // assertEquals(results.length, 5, "expected 5 successful results"); + // assertEquals(errors.length, 5, "expected 5 error results"); + + fetchStub.restore(); +}); \ No newline at end of file diff --git a/__tests__/client/screenshot.test.ts b/__tests__/client/screenshot.test.ts index d711daa..450ef23 100644 --- a/__tests__/client/screenshot.test.ts +++ b/__tests__/client/screenshot.test.ts @@ -1,85 +1,99 @@ -import * as errors from '../../src/errors.js'; -import { ScrapflyClient } from '../../src/client.js'; -import { ScreenshotConfig } from '../../src/screenshotconfig.js'; -import { describe, it, expect, beforeEach, jest } from '@jest/globals'; -import { mockedStream, responseFactory } from '../utils.js'; +import * as errors from '../../src/errors.ts'; +import { ScrapflyClient } from '../../src/client.ts'; +import { ScreenshotConfig } from '../../src/screenshotconfig.ts'; +import { assertEquals, assertRejects } from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { stub } from "https://deno.land/std/testing/mock.ts"; +import { mockedStream, responseFactory } from '../utils.ts'; -describe('screenshot', () => { +Deno.test('screenshot: succeeds', async () => { const KEY = '__API_KEY__'; const client = new ScrapflyClient({ key: KEY }); - - beforeEach(() => { - jest.spyOn(client, 'fetch').mockClear(); // clear all mock meta on each test + const url = 'https://web-scraping.dev/'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const configUrl = new URL(config.url); + // Ensure the URL matches the pattern + assertEquals(configUrl.origin + configUrl.pathname, client.HOST + '/screenshot'); + assertEquals(config.method, 'GET'); + assertEquals(configUrl.searchParams.get('key'), KEY); + assertEquals(configUrl.searchParams.get('url'), url); + assertEquals(Array.from((configUrl.searchParams as any).keys()), ['key', 'url']); + const body = mockedStream(); + return responseFactory(body, { + status: 200, + headers: { + 'content-encoding': 'gzip', + 'content-type': 'image/png', + 'x-scrapfly-upstream-http-code': '200', + 'x-scrapfly-upstream-url': url, + }, + }); }); - it('succeeds', async () => { - const spy = jest.spyOn(client, 'fetch'); - const url = 'https://web-scraping.dev/'; - jest.spyOn(client, 'fetch').mockImplementation(async (config: Request): Promise => { - const configUrl = config[Object.getOwnPropertySymbols(config)[1]].url; - // Ensure the URL matches the pattern - expect(configUrl.origin + configUrl.pathname).toEqual(client.HOST + '/screenshot'); - expect(config.method).toEqual('GET'); - expect(configUrl.searchParams.get('key')).toMatch(KEY); - expect(configUrl.searchParams.get('url')).toMatch(url); - expect(Array.from(configUrl.searchParams.keys())).toEqual(['key', 'url']); - const body = mockedStream(); - return responseFactory(body, { - status: 200, - headers: { - 'content-encoding': 'gzip', - 'content-type': 'image/png', - 'x-scrapfly-upstream-http-code': '200', - 'x-scrapfly-upstream-url': url, - }, - }); - }); + const result = await client.screenshot(new ScreenshotConfig({ url: url })); + assertEquals(result.metadata.extension_name, 'png'); + assertEquals(result.metadata.upstream_url, url); + assertEquals(result.metadata.upstream_status_code, 200); + assertEquals(fetchStub.calls.length, 1); - const result = await client.screenshot(new ScreenshotConfig({ url: url })); - expect(result).toBeDefined(); - expect(result.metadata.extension_name).toBe('png'); - expect(result.metadata.upstream_url).toEqual(url); - expect(result.metadata.upstream_status_code).toBe(200); - expect(spy).toHaveBeenCalledTimes(1); - }); + fetchStub.restore(); +}); - it('fails due to failing upstream response', async () => { - const spy = jest.spyOn(client, 'fetch'); - const url = 'https://domain.com/down-page/'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const body = { - code: 'ERR::SCREENSHOT::UNABLE_TO_TAKE_SCREENSHOT', - error_id: '347bc6cb-1cba-467a-bd06-c932a9e7156d', - http_code: 422, - }; - return responseFactory(body, { - status: 422, - headers: { - 'content-type': 'application/json; charset=utf-8', - }, - }); +Deno.test('screenshot: fails due to failing upstream response', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + const url = 'https://domain.com/down-page/'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const body = { + code: 'ERR::SCREENSHOT::UNABLE_TO_TAKE_SCREENSHOT', + error_id: '347bc6cb-1cba-467a-bd06-c932a9e7156d', + http_code: 422, + }; + return responseFactory(body, { + status: 422, + headers: { + 'content-type': 'application/json; charset=utf-8', + }, }); - await expect(client.screenshot(new ScreenshotConfig({ url }))).rejects.toThrow(errors.ScreenshotApiError); - expect(spy).toHaveBeenCalledTimes(1); }); - it('fails to non html/text web page', async () => { - const spy = jest.spyOn(client, 'fetch'); - const url = 'https://web-scraping.dev/assets/pdf/eula.pdf/'; - jest.spyOn(client, 'fetch').mockImplementation(async (): Promise => { - const body = { - code: 'ERR::SCREENSHOT::INVALID_CONTENT_TYPE', - error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494', - http_code: 422, - }; - return responseFactory(body, { - status: 422, - headers: { - 'content-type': 'application/json; charset=utf-8', - }, - }); + await assertRejects( + async () => { + await client.screenshot(new ScreenshotConfig({ url })); + }, + errors.ScreenshotApiError, + ); + + assertEquals(fetchStub.calls.length, 1); + + fetchStub.restore(); +}); + +Deno.test('screenshot: fails to non html/text web page', async () => { + const KEY = '__API_KEY__'; + const client = new ScrapflyClient({ key: KEY }); + const url = 'https://web-scraping.dev/assets/pdf/eula.pdf/'; + const fetchStub = stub(client, 'fetch', async (config: Request): Promise => { + const body = { + code: 'ERR::SCREENSHOT::INVALID_CONTENT_TYPE', + error_id: 'f0e9a6af-846a-49ab-8321-e21bb12bf494', + http_code: 422, + }; + return responseFactory(body, { + status: 422, + headers: { + 'content-type': 'application/json; charset=utf-8', + }, }); - await expect(client.screenshot(new ScreenshotConfig({ url }))).rejects.toThrow(errors.ScreenshotApiError); - expect(spy).toHaveBeenCalledTimes(1); }); -}); + + await assertRejects( + async () => { + await client.screenshot(new ScreenshotConfig({ url })); + }, + errors.ScreenshotApiError + ); + + assertEquals(fetchStub.calls.length, 1); + + fetchStub.restore(); +}); \ No newline at end of file diff --git a/__tests__/config/extraction.test.ts b/__tests__/config/extraction.test.ts index e0ba317..f18404d 100644 --- a/__tests__/config/extraction.test.ts +++ b/__tests__/config/extraction.test.ts @@ -1,152 +1,166 @@ -import { gzip } from 'zlib'; -import { ExtractionConfig, CompressionFormat } from '../../src/extractionconfig.js'; -import { describe, it, expect } from '@jest/globals'; -import { promisify } from 'util'; -import { ExtractionConfigError } from '../../src/errors.js'; +import { ExtractionConfig, CompressionFormat } from '../../src/extractionconfig.ts'; +import { ExtractionConfigError } from '../../src/errors.ts'; +import { gzipSync } from "node:zlib"; +import { Buffer } from "node:buffer"; +import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts"; -const gzipPromise = promisify(gzip); const input_html = 'very long html file'; const input_content_type = 'text/html'; -describe('extactionconfig', () => { - it('loads', () => { - const config = new ExtractionConfig({ body: input_html, content_type: input_content_type }); - expect(config.body).toBe(input_html); - expect(config.content_type).toBe(input_content_type); - }); +Deno.test('extractionconfig loads', () => { + const config = new ExtractionConfig({ body: input_html, content_type: input_content_type }); + assertEquals(config.body, input_html); + assertEquals(config.content_type, input_content_type); }); -describe('url param generation', () => { - it('basic config', async () => { - const config = new ExtractionConfig({ body: input_html, content_type: input_content_type }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - }); +Deno.test('extractionconfig throws on unknown options', () => { + assertThrows(() => { + new ExtractionConfig({ url: 'http://httpbin.dev/get', foobar: 'baz' } as any); + }, ExtractionConfigError, "Invalid option provided: foobar"); +}); + + + +Deno.test('url param generation: basic config', async () => { + const config = new ExtractionConfig({ body: input_html, content_type: input_content_type }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, }); +}); - it('sets url', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - url: 'https://web-scraping.dev/products', - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - url: 'https://web-scraping.dev/products', - }); +Deno.test('url param generation: sets url', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + url: 'https://web-scraping.dev/products', + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + url: 'https://web-scraping.dev/products', }); +}); - it('sets charset', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - charset: 'utf-8', - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - charset: 'utf-8', - }); +Deno.test('url param generation: sets charset', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + charset: 'utf-8', }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + charset: 'utf-8', + }); +}); - it('sets template', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - template: 'my_template', - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - extraction_template: 'my_template', - }); +Deno.test('url param generation: sets template', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + template: 'my_template', + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + extraction_template: 'my_template', }); +}); - it('sets ephemeral_template', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - ephemeral_template: { source: 'html', selectors: [] }, - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - extraction_template: 'ephemeral:eyJzb3VyY2UiOiJodG1sIiwic2VsZWN0b3JzIjpbXX0', - }); +Deno.test('url param generation: sets ephemeral_template', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + ephemeral_template: { source: 'html', selectors: [] }, + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + extraction_template: 'ephemeral:eyJzb3VyY2UiOiJodG1sIiwic2VsZWN0b3JzIjpbXX0', }); +}); - it('sets extraction_prompt', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - extraction_prompt: 'summarize the document', - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - extraction_prompt: 'summarize the document', - }); +Deno.test('url param generation: sets extraction_prompt', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + extraction_prompt: 'summarize the document', }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + extraction_prompt: 'summarize the document', + }); +}); - it('sets extraction_model', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - extraction_model: 'review_list', - }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - content_type: input_content_type, - extraction_model: 'review_list', - }); +Deno.test('url param generation: sets extraction_model', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + extraction_model: 'review_list', }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + content_type: input_content_type, + extraction_model: 'review_list', + }); +}); - it('compresses body', async () => { +Deno.test({ + name: 'url param generation: compresses body', + async fn() { const config = new ExtractionConfig({ body: input_html, content_type: input_content_type, document_compression_format: CompressionFormat.GZIP, is_document_compressed: false, }); - const params = await config.toApiParams({ key: '1234' }); - expect(params).toEqual({ + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { key: '1234', content_type: input_content_type, }); - expect(config.body).toEqual(await gzipPromise(Buffer.from(input_html as string, 'utf-8'))); + function compressStringSync(input: string): Uint8Array { + const buffer = Buffer.from(input, "utf-8"); + const compressed = gzipSync(buffer); + return new Uint8Array(compressed); + } + const compressedBody = compressStringSync(input_html); + assertEquals(config.body, compressedBody); + }, + sanitizeResources: false, + sanitizeOps: false, +}); + +Deno.test('url param generation: fails to missing compression state with declared compression format', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + document_compression_format: CompressionFormat.GZIP, }); - it('fails to missing compression state with delcated compression format', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - document_compression_format: CompressionFormat.GZIP, - }); + assertThrows(() => { + config.toApiParams({ key: '1234' }); + }, ExtractionConfigError); +}); - await expect(async () => { - await config.toApiParams({ key: '1234' }); - }).rejects.toThrow(ExtractionConfigError); +Deno.test('url param generation: fails to unsupported auto compression format', async () => { + const config = new ExtractionConfig({ + body: input_html, + content_type: input_content_type, + document_compression_format: CompressionFormat.ZSTD, + is_document_compressed: false, }); - it('fails to unsupported auto compression format', async () => { - const config = new ExtractionConfig({ - body: input_html, - content_type: input_content_type, - document_compression_format: CompressionFormat.ZSTD, - is_document_compressed: false - }); - - await expect(async () => { - await config.toApiParams({ key: '1234' }); - }).rejects.toThrow(ExtractionConfigError); - }); -}); + assertThrows(() => { + config.toApiParams({ key: '1234' }); + }, ExtractionConfigError); +}); \ No newline at end of file diff --git a/__tests__/config/scrape.test.ts b/__tests__/config/scrape.test.ts index 1ada32b..808b3bf 100644 --- a/__tests__/config/scrape.test.ts +++ b/__tests__/config/scrape.test.ts @@ -1,518 +1,589 @@ -import { ScrapeConfig, ScreenshotFlags, Format } from '../../src/scrapeconfig.js'; -import { HttpMethod } from '../../src/types.js'; -import { ScrapeConfigError } from '../../src/errors.js'; -import { describe, it, expect } from '@jest/globals'; - -describe('scrapeconfig', () => { - it('loads', () => { - const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); - expect(config.url).toBe('http://httpbin.dev/get'); - }); - - it('allowed methods', () => { - (['GET', 'POST', 'PUT', 'PATCH', 'HEAD'] as HttpMethod[]).forEach((method) => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - method: method, - }); - expect(config.method).toBe(method); - }); - }); +import { ScrapeConfig, ScreenshotFlags, Format, FormatOption } from '../../src/scrapeconfig.ts'; +import { HttpMethod } from '../../src/types.ts'; +import { ScrapeConfigError } from '../../src/errors.ts'; +import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts"; + + +Deno.test('scrapeconfig loads', () => { + const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); + assertEquals(config.url, 'http://httpbin.dev/get'); +}); + +Deno.test('scrapeconfig throws on unknown options', () => { + assertThrows(() => { + new ScrapeConfig({ url: 'http://httpbin.dev/get', foobar: 'baz' } as any); + }, ScrapeConfigError, "Invalid option provided: foobar"); +}); + - it('defaults', () => { - const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); - expect(config.method).toBe('GET'); - expect(config.render_js).toBe(false); - }); - it('POST/PUT/PATCH data->body conversion defaults to form', async () => { +Deno.test('scrapeconfig allowed methods', () => { + (['GET', 'POST', 'PUT', 'PATCH', 'HEAD'] as HttpMethod[]).forEach((method) => { const config = new ScrapeConfig({ url: 'http://httpbin.dev/get', - method: 'POST', - data: { foo: '123', bar: 456 }, + method: method, }); - expect(config.headers['content-type']).toBe('application/x-www-form-urlencoded'); - expect(config.body).toBe('foo=123&bar=456'); + assertEquals(config.method, method); }); +}); - it('POST/PUT/PATCH data->body conversion as json', async () => { - const config = new ScrapeConfig({ +Deno.test('scrapeconfig defaults', () => { + const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); + assertEquals(config.method, 'GET'); + assertEquals(config.render_js, false); +}); + +Deno.test('scrapeconfig POST/PUT/PATCH data->body conversion defaults to form', async () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + method: 'POST', + data: { foo: '123', bar: 456 }, + headers: {}, + }); + assertEquals((config.headers || {})['content-type'], 'application/x-www-form-urlencoded'); + assertEquals(config.body, 'foo=123&bar=456'); +}); + +Deno.test('scrapeconfig POST/PUT/PATCH data->body conversion as json', async () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + method: 'POST', + data: { foo: '123', bar: 456 }, + headers: { 'content-type': 'application/json' }, + }); + assertEquals((config.headers || {})['content-type'], 'application/json'); + assertEquals(config.body, '{"foo":"123","bar":456}'); +}); + +Deno.test('scrapeconfig POST/PUT/PATCH body defaults as content-type text/plain', async () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + method: 'POST', + body: 'foo+bar', + }); + assertEquals((config.headers || {})['content-type'], 'text/plain'); + assertEquals(config.body, 'foo+bar'); +}); + +Deno.test('scrapeconfig POST/PUT/PATCH data encodes when formdata content-type is set', async () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + method: 'POST', + data: { foo: 1, bar: 'mojito please' }, + headers: { 'content-type': 'application/x-www-form-urlencoded' }, + }); + assertEquals((config.headers || {})['content-type'], 'application/x-www-form-urlencoded'); + assertEquals(config.body, 'foo=1&bar=mojito+please'); +}); + +Deno.test('scrapeconfig POST/PUT/PATCH data throws when unsupported content-type is set', async () => { + assertThrows(() => { + new ScrapeConfig({ url: 'http://httpbin.dev/get', method: 'POST', - data: { foo: '123', bar: 456 }, - headers: { 'content-type': 'application/json' }, + data: { foo: 1, bar: 'mojito please' }, + headers: { 'content-type': 'does/not/exist' }, }); - expect(config.headers['content-type']).toBe('application/json'); - expect(config.body).toBe('{"foo":"123","bar":456}'); - }); + }, ScrapeConfigError); +}); - it('POST/PUT/PATCH body defaults as content-type text/plain', async () => { - const config = new ScrapeConfig({ +Deno.test('config invalid: data and body set together', async () => { + assertThrows(() => { + new ScrapeConfig({ url: 'http://httpbin.dev/get', method: 'POST', - body: 'foo+bar', + data: { foo: '123' }, + body: '{"foo": "123"}', }); - expect(config.headers['content-type']).toBe('text/plain'); - expect(config.body).toBe('foo+bar'); + }, ScrapeConfigError); +}); + +Deno.test('url param generation: basic config', () => { + const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: 'http://httpbin.dev/get', }); - it('POST/PUT/PATCH data encodes when formdata content-type is set', async () => { +}); + +Deno.test('url param generation: country keeps formatting as is', () => { + const countries = ['us', 'us,ca,mx', 'us:1,ca:5,mx:3,-gb']; + countries.forEach((country) => { const config = new ScrapeConfig({ url: 'http://httpbin.dev/get', - method: 'POST', - data: { foo: 1, bar: 'mojito please' }, - headers: { 'content-type': 'application/x-www-form-urlencoded' }, - }); - expect(config.headers['content-type']).toBe('application/x-www-form-urlencoded'); - expect(config.body).toBe('foo=1&bar=mojito+please'); - }); - it('POST/PUT/PATCH data throws when unsupported content-type is set', async () => { - expect(() => { - new ScrapeConfig({ - url: 'http://httpbin.dev/get', - method: 'POST', - data: { foo: 1, bar: 'mojito please' }, - headers: { 'content-type': 'does/not/exist' }, - }); - }).toThrow(ScrapeConfigError); - }); -}); - -describe('config invalid', () => { - it('data and body set together', async () => { - expect(() => { - new ScrapeConfig({ - url: 'http://httpbin.dev/get', - method: 'POST', - data: { foo: '123' }, - body: '{"foo": "123"}', - }); - }).toThrow(ScrapeConfigError); - }); -}); - -describe('url param generation', () => { - it('basic config', () => { - const config = new ScrapeConfig({ url: 'http://httpbin.dev/get' }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', + country: country, }); + assertEquals(config.toApiParams({ key: '1234' }).country, country); }); +}); - it('country keeps formatting as is', () => { - const countries = ['us', 'us,ca,mx', 'us:1,ca:5,mx:3,-gb']; - countries.forEach((country) => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - country: country, - }); - expect(config.toApiParams({ key: '1234' }).country).toBe(country); - }); +Deno.test('url param generation: headers formatted as headers[key]=value', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + headers: { 'x-test': 'test', 'Content-Type': 'mock' }, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'headers[x-test]': 'test', + 'headers[content-type]': 'mock', }); +}); - it('headers formatted as headers[key]=value', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - headers: { 'x-test': 'test', 'Content-Type': 'mock' }, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'headers[x-test]': 'test', - 'headers[content-type]': 'mock', - }); +Deno.test('url param generation: headers override duplicates', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + headers: { 'x-test': 'test', 'X-Test': 'mock' }, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'headers[x-test]': 'mock', }); +}); - it('headers override duplicates', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - headers: { 'x-test': 'test', 'X-Test': 'mock' }, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'headers[x-test]': 'mock', - }); +Deno.test('url param generation: headers are not case sensitive', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + headers: { 'x-test': 'test', 'X-Test': 'mock' }, }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'headers[x-test]': 'mock', + }); +}); - it('headers are not case sensitive', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - headers: { 'x-test': 'test', 'X-Test': 'mock' }, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'headers[x-test]': 'mock', - }); +Deno.test('url param generation: cookies added to Cookie header', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + cookies: { 'x-test': 'test', 'X-Test': 'mock' }, }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'headers[cookie]': 'x-test=mock', + }); +}); - it('cookies added to Cookie header', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - cookies: { 'x-test': 'test', 'X-Test': 'mock' }, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'headers[cookie]': 'x-test=mock', - }); +Deno.test('url param generation: cookies extend Cookie header', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + cookies: { 'x-test': 'test', 'X-Test': 'mock' }, + headers: { cookie: 'foo=bar' }, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'headers[cookie]': 'foo=bar; x-test=mock', }); +}); - it('cookies extend Cookie header', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - cookies: { 'x-test': 'test', 'X-Test': 'mock' }, - headers: { cookie: 'foo=bar' }, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'headers[cookie]': 'foo=bar; x-test=mock', - }); +Deno.test('url param generation: complex urls pass as is', () => { + const config = new ScrapeConfig({ + url: 'https://httpbin.dev/anything/?website=https://httpbin.dev/anything', }); - it('complex urls pass as is', () => { - const config = new ScrapeConfig({ - url: 'https://httpbin.dev/anything/?website=https://httpbin.dev/anything', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'https://httpbin.dev/anything/?website=https://httpbin.dev/anything', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'https://httpbin.dev/anything/?website=https://httpbin.dev/anything', }); - it('screenshots converted to params', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - screenshots: { everything: 'fullpage' }, - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - render_js: true, - url: 'http://httpbin.dev/get', - 'screenshots[everything]': 'fullpage', - }); +}); + +Deno.test('url param generation: screenshots converted to params', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + screenshots: { everything: 'fullpage' }, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + render_js: true, + url: 'http://httpbin.dev/get', + 'screenshots[everything]': 'fullpage', }); - it('screenshot flags converted to params', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - screenshots: { everything: 'fullpage' }, - screenshot_flags: [ - ScreenshotFlags.LOAD_IMAGES, // Enable image rendering with the request, adds extra usage for the bandwidth consumed - ScreenshotFlags.DARK_MODE, // Enable dark mode display - ScreenshotFlags.BLOCK_BANNERS, // Block cookies banners and overlay that cover the screen - ScreenshotFlags.HIGH_QUALITY, // No compression on the output image - ScreenshotFlags.LOAD_IMAGES, // Render the page in the print mode - ], - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - 'screenshots[everything]': 'fullpage', - screenshot_flags: 'load_images,dark_mode,block_banners,high_quality,load_images', - render_js: true, - }); +}); + +Deno.test('url param generation: screenshot flags converted to params', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + screenshots: { everything: 'fullpage' }, + screenshot_flags: [ + ScreenshotFlags.LOAD_IMAGES, + ScreenshotFlags.DARK_MODE, + ScreenshotFlags.BLOCK_BANNERS, + ScreenshotFlags.HIGH_QUALITY, + ScreenshotFlags.LOAD_IMAGES, + ], + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + 'screenshots[everything]': 'fullpage', + screenshot_flags: 'load_images,dark_mode,block_banners,high_quality,load_images', + render_js: true, }); - it('asp enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - asp: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - asp: true, - }); +}); + +Deno.test('url param generation: format options converted to format extension', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + format: Format.MARKDOWN, + format_options: [FormatOption.NO_IMAGES, FormatOption.NO_LINKS], }); - it('dns enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - dns: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - dns: true, - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + format: "markdown:no_images,no_links" }); - it('ssl enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - ssl: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - ssl: true, - }); +}); + + + +Deno.test('url param generation: asp enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + asp: true, }); - it('tags set', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - tags: ['foo', 'bar', 'gaz'], - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - tags: 'foo,bar,gaz', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + asp: true, }); - it('format set', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - format: Format.MARKDOWN, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - format: 'markdown', - }); +}); + +Deno.test('url param generation: dns enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + dns: true, }); - it('debug sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - debug: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - debug: true, - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + dns: true, }); - it('lang sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - lang: ['en', 'fr', 'lt'], - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - lang: 'en,fr,lt', - }); +}); + +Deno.test('url param generation: ssl enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + ssl: true, }); - it('os sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - os: 'linux', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - os: 'linux', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + ssl: true, }); - it('proxy_pool sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - proxy_pool: 'public_residential_pool', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - proxy_pool: 'public_residential_pool', - }); +}); + +Deno.test('url param generation: tags set', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + tags: ['foo', 'bar', 'gaz'], }); - it('session sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - session: 'foo123', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - session: 'foo123', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + tags: 'foo,bar,gaz', }); - it('session_sticky_proxy sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - session: 'foo123', - session_sticky_proxy: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - session: 'foo123', - session_sticky_proxy: true, - }); +}); + +Deno.test('url param generation: format set', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + format: Format.MARKDOWN, }); - it('session_sticky_proxy ignored with no session', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - session_sticky_proxy: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + format: 'markdown', }); +}); - it('correlation id sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - correlation_id: '1234', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - correlation_id: '1234', - }); +Deno.test('url param generation: debug sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + debug: true, }); - it('webhook enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - webhook: 'snailmail', - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - webhook_name: 'snailmail', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + debug: true, }); +}); - it('timeout enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - timeout: 10, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - timeout: 10, - }); +Deno.test('url param generation: lang sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + lang: ['en', 'fr', 'lt'], }); - it('retry disables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - retry: false, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - retry: false, - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + lang: 'en,fr,lt', }); - it('cache enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - cache: true, - cache_ttl: 60, - cache_clear: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - cache: true, - cache_ttl: 60, - cache_clear: true, - }); +}); + +Deno.test('url param generation: os sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + os: 'linux', + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + os: 'linux', }); +}); - it('auto_scroll enables', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - auto_scroll: true, - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - auto_scroll: true, - render_js: true, - }); +Deno.test('url param generation: proxy_pool sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + proxy_pool: 'public_residential_pool', }); - it('wait_for_selector sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - wait_for_selector: '#foo', - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - wait_for_selector: '#foo', - render_js: true, - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + proxy_pool: 'public_residential_pool', }); - it('rendering_wait sets', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - rendering_wait: 10, - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - rendering_wait: 10, - render_js: true, - }); +}); + +Deno.test('url param generation: session sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + session: 'foo123', }); - it('render_js optionals ignored when disabled', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - wait_for_selector: '.foo', - screenshots: { all: 'fullpage' }, - js_scenario: [], - js: '', - rendering_wait: 10, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + session: 'foo123', }); +}); - it('cache args are ignored when cache disabled', () => { - const config = new ScrapeConfig({ - url: 'http://httpbin.dev/get', - cache: false, - cache_ttl: 60, - cache_clear: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - }); +Deno.test('url param generation: session_sticky_proxy sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + session: 'foo123', + session_sticky_proxy: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + session: 'foo123', + session_sticky_proxy: true, }); +}); - it('js encodes', () => { - const code = 'return document.querySelectorAll(".review p").map(p=>p.outerText))'; - const config = new ScrapeConfig({ - url: 'https://web-scraping.dev/product/1', - js: code, - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - url: 'https://web-scraping.dev/product/1', - key: '1234', - render_js: true, - js: 'cmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZXZpZXcgcCIpLm1hcChwPT5wLm91dGVyVGV4dCkp', - }); +Deno.test('url param generation: session_sticky_proxy ignored with no session', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + session_sticky_proxy: true, }); - it('js scenario encodes', () => { - const scenario = [ - { wait_for_selector: { selector: '.review' } }, - { click: { selector: '#load-more-reviews' } }, - { wait_for_navigation: {} }, - { - execute: { - script: "[...document.querySelectorAll('.review p')].map(p=>p.outerText)", - }, - }, - ]; - const config = new ScrapeConfig({ - url: 'https://web-scraping.dev/product/1', - js_scenario: scenario, - render_js: true, - }); - expect(config.toApiParams({ key: '1234' })).toEqual({ - url: 'https://web-scraping.dev/product/1', - key: '1234', - render_js: true, - js_scenario: - 'W3sid2FpdF9mb3Jfc2VsZWN0b3IiOnsic2VsZWN0b3IiOiIucmV2aWV3In19LHsiY2xpY2siOnsic2VsZWN0b3IiOiIjbG9hZC1tb3JlLXJldmlld3MifX0seyJ3YWl0X2Zvcl9uYXZpZ2F0aW9uIjp7fX0seyJleGVjdXRlIjp7InNjcmlwdCI6IlsuLi5kb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcucmV2aWV3IHAnKV0ubWFwKHA9PnAub3V0ZXJUZXh0KSJ9fV0', - }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + }); +}); + +Deno.test('url param generation: correlation id sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + correlation_id: '1234', + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + correlation_id: '1234', + }); +}); + +Deno.test('url param generation: webhook enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + webhook: 'snailmail', + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + webhook_name: 'snailmail', + }); +}); + +Deno.test('url param generation: timeout enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + timeout: 10, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + timeout: 10, }); }); + +Deno.test('url param generation: retry disables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + retry: false, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + retry: false, + }); +}); + +Deno.test('url param generation: cache enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + cache: true, + cache_ttl: 60, + cache_clear: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + cache: true, + cache_ttl: 60, + cache_clear: true, + }); +}); + +Deno.test('url param generation: auto_scroll enables', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + auto_scroll: true, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + auto_scroll: true, + render_js: true, + }); +}); + +Deno.test('url param generation: wait_for_selector sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + wait_for_selector: '#foo', + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + wait_for_selector: '#foo', + render_js: true, + }); +}); + +Deno.test('url param generation: rendering_wait sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); +}); + +Deno.test('url param generation: rendering_wait sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); +}); + +Deno.test('url param generation: rendering_wait sets', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + rendering_wait: 10, + render_js: true, + }); +}); + +Deno.test('url param generation: render_js optionals ignored when disabled', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + wait_for_selector: '.foo', + screenshots: { all: 'fullpage' }, + js_scenario: [], + js: '', + rendering_wait: 10, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + }); +}); + +Deno.test('url param generation: cache args are ignored when cache disabled', () => { + const config = new ScrapeConfig({ + url: 'http://httpbin.dev/get', + cache: false, + cache_ttl: 60, + cache_clear: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + key: '1234', + url: 'http://httpbin.dev/get', + }); +}); + +Deno.test('url param generation: js encodes', () => { + const code = 'return document.querySelectorAll(".review p").map(p=>p.outerText))'; + const config = new ScrapeConfig({ + url: 'https://web-scraping.dev/product/1', + js: code, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + url: 'https://web-scraping.dev/product/1', + key: '1234', + render_js: true, + js: 'cmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZXZpZXcgcCIpLm1hcChwPT5wLm91dGVyVGV4dCkp', + }); +}); + +Deno.test('url param generation: js scenario encodes', () => { + const scenario = [ + { wait_for_selector: { selector: '.review' } }, + { click: { selector: '#load-more-reviews' } }, + { wait_for_navigation: {} }, + { + execute: { + script: "[...document.querySelectorAll('.review p')].map(p=>p.outerText)", + }, + }, + ]; + const config = new ScrapeConfig({ + url: 'https://web-scraping.dev/product/1', + js_scenario: scenario, + render_js: true, + }); + assertEquals(config.toApiParams({ key: '1234' }), { + url: 'https://web-scraping.dev/product/1', + key: '1234', + render_js: true, + js_scenario: + 'W3sid2FpdF9mb3Jfc2VsZWN0b3IiOnsic2VsZWN0b3IiOiIucmV2aWV3In19LHsiY2xpY2siOnsic2VsZWN0b3IiOiIjbG9hZC1tb3JlLXJldmlld3MifX0seyJ3YWl0X2Zvcl9uYXZpZ2F0aW9uIjp7fX0seyJleGVjdXRlIjp7InNjcmlwdCI6IlsuLi5kb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcucmV2aWV3IHAnKV0ubWFwKHA9PnAub3V0ZXJUZXh0KSJ9fV0', + }); +}); \ No newline at end of file diff --git a/__tests__/config/screenshot.test.ts b/__tests__/config/screenshot.test.ts index d000d22..ff51513 100644 --- a/__tests__/config/screenshot.test.ts +++ b/__tests__/config/screenshot.test.ts @@ -1,228 +1,232 @@ -import { ScreenshotConfig, Format, Options } from '../../src/screenshotconfig.js'; -import { ScreenshotConfigError } from '../../src/errors.js'; -import { describe, it, expect } from '@jest/globals'; +import { ScreenshotConfig, Format, Options } from '../../src/screenshotconfig.ts'; +import { ScreenshotConfigError } from '../../src/errors.ts'; +import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts"; -describe('scrapeconfig', () => { - it('loads', () => { - const config = new ScreenshotConfig({ url: 'http://httpbin.dev/get' }); - expect(config.url).toBe('http://httpbin.dev/get'); - }); -}); +const input_url = 'http://httpbin.dev/get'; -describe('config invalid', () => { - it('sets invalid format', async () => { - expect(() => { - new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - format: 'invalid' as any, - }); - }).toThrow(ScreenshotConfigError); - }); +Deno.test('screenshotconfig loads', () => { + const config = new ScreenshotConfig({ url: input_url }); + assertEquals(config.url, input_url); +}); - it('sets invalid options', async () => { - expect(() => { - new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - options: ['invalid', 'invalid_too'] as any, - }); - }).toThrow(ScreenshotConfigError); - }); +Deno.test('screenshotconfig throws on unknown options', () => { + assertThrows(() => { + new ScreenshotConfig({ url: 'http://httpbin.dev/get', foobar: 'baz' } as any); + }, ScreenshotConfigError, "Invalid option provided: foobar"); }); -describe('url param generation', () => { - it('basic config', () => { - const config = new ScreenshotConfig({ url: 'http://httpbin.dev/get' }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - }); - }); - it('sets format', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - format: Format.PNG, + +Deno.test('config invalid: sets invalid format', () => { + assertThrows(() => { + new ScreenshotConfig({ + url: input_url, + format: 'invalid' as any, }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - format: 'png', + }, ScreenshotConfigError); +}); + +Deno.test('config invalid: sets invalid options', () => { + assertThrows(() => { + new ScreenshotConfig({ + url: input_url, + options: ['invalid', 'invalid_too'] as any, }); + }, ScreenshotConfigError); +}); + +Deno.test('url param generation: basic config', () => { + const config = new ScreenshotConfig({ url: input_url }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, }); +}); - it('sets capture', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - capture: 'fullpage', - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - capture: 'fullpage', - }); +Deno.test('url param generation: sets format', () => { + const config = new ScreenshotConfig({ + url: input_url, + format: Format.PNG, }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + format: 'png', + }); +}); - it('sets resolution', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - resolution: '1920x1080', - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - resolution: '1920x1080', - }); +Deno.test('url param generation: sets capture', () => { + const config = new ScreenshotConfig({ + url: input_url, + capture: 'fullpage', + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + capture: 'fullpage', }); +}); - it('sets country', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - country: 'us,ca,mx', - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - country: 'us,ca,mx', - }); +Deno.test('url param generation: sets resolution', () => { + const config = new ScreenshotConfig({ + url: input_url, + resolution: '1920x1080', + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + resolution: '1920x1080', }); +}); - it('sets timeout', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - timeout: 60000, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - timeout: 60000, - }); +Deno.test('url param generation: sets country', () => { + const config = new ScreenshotConfig({ + url: input_url, + country: 'us,ca,mx', + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + country: 'us,ca,mx', }); +}); - it('sets rendering_wait', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - rendering_wait: 5000, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - rendering_wait: 5000, - }); +Deno.test('url param generation: sets timeout', () => { + const config = new ScreenshotConfig({ + url: input_url, + timeout: 60000, + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + timeout: 60000, }); +}); - it('sets wait_for_selector', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - wait_for_selector: '//div[@class="prouct"]', - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - wait_for_selector: '//div[@class="prouct"]', - }); +Deno.test('url param generation: sets rendering_wait', () => { + const config = new ScreenshotConfig({ + url: input_url, + rendering_wait: 5000, + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + rendering_wait: 5000, }); +}); - it('sets options', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - options: [Options.BLOCK_BANNERS, Options.DARK_MODE, Options.LOAD_IMAGES, Options.PRINT_MEDIA_FORMAT], - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - options: 'block_banners,dark_mode,load_images,print_media_format', - }); +Deno.test('url param generation: sets wait_for_selector', () => { + const config = new ScreenshotConfig({ + url: input_url, + wait_for_selector: '//div[@class="product"]', }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + wait_for_selector: '//div[@class="product"]', + }); +}); - it('sets auto_scroll', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - auto_scroll: true, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - auto_scroll: true, - }); +Deno.test('url param generation: sets options', () => { + const config = new ScreenshotConfig({ + url: input_url, + options: [Options.BLOCK_BANNERS, Options.DARK_MODE, Options.LOAD_IMAGES, Options.PRINT_MEDIA_FORMAT], }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + options: 'block_banners,dark_mode,load_images,print_media_format', + }); +}); - it('sets js', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - js: 'return document.querySelectorAll(".review p").map(p=>p.outerText))', - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - js: 'cmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZXZpZXcgcCIpLm1hcChwPT5wLm91dGVyVGV4dCkp', - }); +Deno.test('url param generation: sets auto_scroll', () => { + const config = new ScreenshotConfig({ + url: input_url, + auto_scroll: true, }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + auto_scroll: true, + }); +}); - it('sets cache', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - cache: true, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - cache: true, - }); +Deno.test('url param generation: sets js', () => { + const config = new ScreenshotConfig({ + url: input_url, + js: 'return document.querySelectorAll(".review p").map(p => p.outerText)', }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + js: 'cmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZXZpZXcgcCIpLm1hcChwID0-IHAub3V0ZXJUZXh0KQ', + }); +}); - it('sets cache_ttl', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - cache: true, - cache_ttl: true, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - cache: true, - cache_ttl: true, - }); +Deno.test('url param generation: sets cache', () => { + const config = new ScreenshotConfig({ + url: input_url, + cache: true, }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + cache: true, + }); +}); - it('sets cache_clear', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - cache: true, - cache_clear: true, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - cache: true, - cache_clear: true, - }); +Deno.test('url param generation: sets cache_ttl', () => { + const config = new ScreenshotConfig({ + url: input_url, + cache: true, + cache_ttl: 3600, }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + cache: true, + cache_ttl: 3600, + }); +}); - it('ignores cache_ttl and cache_clear with cache disabled', () => { - const config = new ScreenshotConfig({ - url: 'http://httpbin.dev/get', - cache: false, - cache_ttl: true, - cache_clear: true, - }); - const params = config.toApiParams({ key: '1234' }); - expect(params).toEqual({ - key: '1234', - url: 'http://httpbin.dev/get', - }); +Deno.test('url param generation: sets cache_clear', () => { + const config = new ScreenshotConfig({ + url: input_url, + cache: true, + cache_clear: true, + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + cache: true, + cache_clear: true, }); }); + +Deno.test('url param generation: ignores cache_ttl and cache_clear with cache disabled', () => { + const config = new ScreenshotConfig({ + url: input_url, + cache: false, + cache_ttl: 3600, + cache_clear: true, + }); + const params = config.toApiParams({ key: '1234' }); + assertEquals(params, { + key: '1234', + url: input_url, + }); +}); \ No newline at end of file diff --git a/__tests__/result.test.ts b/__tests__/result.test.ts index c16b31f..2f30ee7 100644 --- a/__tests__/result.test.ts +++ b/__tests__/result.test.ts @@ -1,26 +1,22 @@ -import * as cheerio from 'cheerio'; -import * as fs from 'fs'; -import { ScrapeResult } from '../src/result.js'; -import * as errors from '../src/errors.js'; -import { describe, it, expect, jest } from '@jest/globals'; +import { ScrapeResult } from '../src/result.ts'; +import * as errors from '../src/errors.ts'; +import { assertEquals, assertThrows } from "https://deno.land/std@0.224.0/assert/mod.ts"; -describe('cheerio selector', () => { - it('lazy loads and caches itself', () => { - const response = JSON.parse(fs.readFileSync('__tests__/data/response_html_success.json', 'utf8')); - const result = new ScrapeResult(response); - const spy = jest.spyOn(cheerio, 'load'); - expect(result.selector('h1').text()).toEqual('Herman Melville - Moby-Dick'); - // make sure calling it twice performs the same - expect(result.selector('h1').text()).toEqual('Herman Melville - Moby-Dick'); - // cheerio.load is called exactly once - means it's cached - expect(spy).toHaveBeenCalledTimes(1); - spy.mockRestore(); - }); - it('throws ContentTypeError when accessing .selector on JSON data', () => { - const response = JSON.parse(fs.readFileSync('__tests__/data/response_json_success.json', 'utf8')); - const result = new ScrapeResult(response); - expect(() => { - result.selector('h1').text(); - }).toThrow(errors.ContentTypeError); - }); +Deno.test('cheerio selector lazy loads and caches itself', async () => { + const responseHtmlSuccess = JSON.parse(await Deno.readTextFile('__tests__/data/response_html_success.json')); + const result = new ScrapeResult(responseHtmlSuccess); + // Perform assertions + assertEquals(result.selector('h1').text(), 'Herman Melville - Moby-Dick'); + // Make sure calling it twice performs the same + assertEquals(result.selector('h1').text(), 'Herman Melville - Moby-Dick'); + // cheerio.load is called exactly once - means it's cached }); + +Deno.test('throws ContentTypeError when accessing .selector on JSON data', async () => { + const responseJsonSuccess = JSON.parse(await Deno.readTextFile('__tests__/data/response_json_success.json')); + const result = new ScrapeResult(responseJsonSuccess); + + assertThrows(() => { + result.selector('h1').text(); + }, errors.ContentTypeError); +}); \ No newline at end of file diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index 365bcc7..af7e0d6 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -1,18 +1,106 @@ -import { urlsafe_b64encode } from '../src/utils.js'; -import { describe, it, expect } from '@jest/globals'; - -describe('urlsafe_b64encode', () => { - it('should encode a string to base64', () => { - expect(urlsafe_b64encode('hello+foo/bar=====')).toBe('aGVsbG8rZm9vL2Jhcj09PT09'); - }); - it('multiple + characters should be encoded correctly -', () => { - const inp ='const is_greater_than_1 = 45 >= 5 ? true : false\nconst is_greater_than_2 = 6435 >= 5 ? true : false'; - const expected = 'Y29uc3QgaXNfZ3JlYXRlcl90aGFuXzEgPSA0NSA-PSA1ID8gdHJ1ZSA6IGZhbHNlCmNvbnN0IGlzX2dyZWF0ZXJfdGhhbl8yID0gNjQzNSA-PSA1ID8gdHJ1ZSA6IGZhbHNl' - expect(urlsafe_b64encode(inp)).toBe(expected); - }); - it('multiple / characters should be encoded correctly as _', () => { - const inp ='const has_env1 = window.env1 ? true : false;\nconst has_env2 = window.env2 ? true : false;'; - const expected = 'Y29uc3QgaGFzX2VudjEgPSB3aW5kb3cuZW52MSA_IHRydWUgOiBmYWxzZTsKY29uc3QgaGFzX2VudjIgPSB3aW5kb3cuZW52MiA_IHRydWUgOiBmYWxzZTs' - expect(urlsafe_b64encode(inp)).toBe(expected); - }); +import { urlsafe_b64encode, fetchRetry } from '../src/utils.ts'; +import { assertEquals, assertRejects } from "jsr:@std/assert"; +import { stub } from "https://deno.land/std@0.224.0/testing/mock.ts"; + +Deno.test("urlsafe_b64encode should encode a string to base64", () => { + assertEquals(urlsafe_b64encode('hello+foo/bar====='), 'aGVsbG8rZm9vL2Jhcj09PT09'); +}); + +Deno.test("urlsafe_b64encode multiple + characters should be encoded correctly", () => { + const inp = 'const is_greater_than_1 = 45 >= 5 ? true : false\nconst is_greater_than_2 = 6435 >= 5 ? true : false'; + const expected = 'Y29uc3QgaXNfZ3JlYXRlcl90aGFuXzEgPSA0NSA-PSA1ID8gdHJ1ZSA6IGZhbHNlCmNvbnN0IGlzX2dyZWF0ZXJfdGhhbl8yID0gNjQzNSA-PSA1ID8gdHJ1ZSA6IGZhbHNl'; + assertEquals(urlsafe_b64encode(inp), expected); +}); + +Deno.test("urlsafe_b64encode multiple / characters should be encoded correctly as _", () => { + const inp = 'const has_env1 = window.env1 ? true : false;\nconst has_env2 = window.env2 ? true : false;'; + const expected = 'Y29uc3QgaGFzX2VudjEgPSB3aW5kb3cuZW52MSA_IHRydWUgOiBmYWxzZTsKY29uc3QgaGFzX2VudjIgPSB3aW5kb3cuZW52MiA_IHRydWUgOiBmYWxzZTs'; + assertEquals(urlsafe_b64encode(inp), expected); +}); + +Deno.test('fetchRetry: succeeds on first attempt', async () => { + const fetchStub = stub(globalThis, 'fetch', async (input: RequestInfo | URL, init?: RequestInit): Promise => { + return new Response('Success', { status: 200 }); + }); + + const request = new Request('https://example.com'); + const response = await fetchRetry(request); + + assertEquals(await response.text(), 'Success'); + assertEquals(response.status, 200); + + fetchStub.restore(); +}); + +Deno.test('fetchRetry: retries on 500 and succeeds', async () => { + let callCount = 0; + const fetchStub = stub(globalThis, 'fetch', async (input: RequestInfo | URL, init?: RequestInit): Promise => { + callCount++; + if (callCount < 2) { + return new Response('Internal Server Error', { status: 500 }); + } else { + return new Response('Success', { status: 200 }); + } + }); + + const request = new Request('https://example.com'); + const response = await fetchRetry(request); + + assertEquals(await response.text(), 'Success'); + assertEquals(response.status, 200); + assertEquals(callCount, 2); + + fetchStub.restore(); +}); + +Deno.test('fetchRetry: does not retry 4xx', async () => { + let callCount = 0; + const fetchStub = stub(globalThis, 'fetch', async (input: RequestInfo | URL, init?: RequestInit): Promise => { + callCount++; + return new Response('bad request', { status: 422 }); + }); + + const request = new Request('https://example.com'); + const response = await fetchRetry(request); + + assertEquals(await response.text(), 'bad request'); + assertEquals(response.status, 422); + assertEquals(callCount, 1); + + fetchStub.restore(); +}); + +Deno.test('fetchRetry: fails after max retries', async () => { + let callCount = 0; + const fetchStub = stub(globalThis, 'fetch', async (input: RequestInfo | URL, init?: RequestInit): Promise => { + callCount++; + return new Response('Internal Server Error', { status: 500 }); + }); + + const request = new Request('https://example.com'); + + await assertRejects( + async () => { + await fetchRetry(request, {}, 3, 1000); + }, + Error, + 'Fetch failed with status: 500' + ); + + assertEquals(callCount, 3); + + fetchStub.restore(); }); + +Deno.test('fetchRetry: fails due to timeout', async () => { + + const request = new Request('https://httpbin.dev/delay/3'); + + await assertRejects( + async () => { + await fetchRetry(request, {}, 3, 100, 1000); // Set a short timeout for the test + }, + Error, + 'The signal has been aborted' + ); +}); \ No newline at end of file diff --git a/build.ts b/build.ts new file mode 100644 index 0000000..5295f50 --- /dev/null +++ b/build.ts @@ -0,0 +1,66 @@ +import { build, emptyDir } from "@deno/dnt"; +import { rollup } from "npm:rollup"; +import resolve from "npm:@rollup/plugin-node-resolve"; +import commonjs from "npm:@rollup/plugin-commonjs"; +import { terser } from "npm:rollup-plugin-terser"; + +async function bundleForBrowser() { + console.log("Bundling for browser..."); + const bundle = await rollup({ + input: "npm/esm/main.js", + plugins: [ + resolve({ browser: true, preferBuiltins: false }), + commonjs(), + terser(), + ], + logLevel: 'debug', + }); + + console.log("Writing to npm/dist/bundle.js"); + await bundle.write({ + file: "npm/dist/bundle.js", + format: "esm", + sourcemap: true, + }); + console.log("Closing Rollup"); + await bundle.close(); +} + +await emptyDir("./npm"); + +const { version, description } = JSON.parse(Deno.readTextFileSync("deno.json")); + +await build({ + entryPoints: ["./src/main.ts"], + outDir: "./npm", + shims: { + deno: true, + }, + // typeCheck: false, // to disable type checking + test: false, // XXX: try running the test but there's 1 unsolvable issue with RequestInfo + package: { + name: "scrapfly-sdk", + version: version, + description: description, + license: "BSD", + type: "module", + keywords: ["web scraping", "scrapfly", "api", "sdk"], + repository: { + type: "git", + url: "git+https://github.com/scrapfly/typescript-scrapfly.git", + }, + bugs: { + url: "https://github.com/scrapfly/typescript-scrapfly/issues", + }, + homepage: "https://scrapfly.io/", + main: "./esm/src/main.js", // Point to the ESM output + types: "./esm/src/main.d.ts", // Point to the TypeScript declarations + }, + postBuild: async () => { + Deno.copyFileSync("LICENSE", "npm/LICENSE"); + Deno.copyFileSync("README.md", "npm/README.md"); + await bundleForBrowser(); + }, +}); + +Deno.exit(); \ No newline at end of file diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..6ba66fb --- /dev/null +++ b/deno.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "lib": ["deno.ns", "dom"] + }, + "name": "@scrapfly/scrapfly-sdk", + "exports": "./src/main.ts", + "version": "0.6.0", + "description": "SDK for Scrapfly.io API for web scraping, screenshotting and data extraction", + "tasks": { + "start": "deno run --allow-net --allow-read src/main.ts", + "test": "deno test --allow-net --allow-read __tests__", + "fmt": "deno fmt" + }, + "fmt": { + "include": ["src/**/*.ts", "tests/**/*.ts"], + "useTabs": false, + "lineWidth": 120, + "indentWidth": 2, + "singleQuote": true + }, + "lint": { + "include": ["src/**/*.ts", "tests/**/*.ts"], + "rules": { + "exclude": ["no-explicit-any"] + } + }, + "imports": { + "@deno/dnt": "jsr:@deno/dnt@^0.41.2" + } +} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index c824664..0000000 --- a/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - testEnvironment: 'node', - preset: 'ts-jest/presets/default-esm', - testPathIgnorePatterns: ['/node_modules/', '/.history/', '/build/', '/__tests__/utils.ts'], - transform: { - '^.+\\.m?[tj]s?$': ['ts-jest', { useESM: true }], - }, - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.(m)?js$': '$1', - }, - testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(m)?ts$', - coverageDirectory: 'coverage', - collectCoverageFrom: ['src/**/*.ts', 'src/**/*.mts', '!src/**/*.d.ts', '!src/**/*.d.mts'], -}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 788ed47..0000000 --- a/package-lock.json +++ /dev/null @@ -1,8074 +0,0 @@ -{ - "name": "scrapfly-sdk", - "version": "0.5.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "scrapfly-sdk", - "version": "0.5.1", - "license": "BSD", - "dependencies": { - "@types/cheerio": "^0.22.31", - "cheerio": "^1.0.0-rc.12", - "fetch-retry": "^6.0.0", - "tslib": "~2.5" - }, - "devDependencies": { - "@types/node": "~18", - "@typescript-eslint/eslint-plugin": "~5.59", - "@typescript-eslint/parser": "~5.59", - "eslint": "~8.38", - "eslint-config-prettier": "~8.8", - "eslint-plugin-jest": "~27.2", - "jest": "~29.5", - "prettier": "~2.8", - "rimraf": "~5.0", - "ts-api-utils": "~0.0.44", - "ts-jest": "~29.1", - "typescript": "^5.1.6" - }, - "engines": { - "node": ">= 18 <19" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz", - "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/cheerio": { - "version": "0.22.35", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.35.tgz", - "integrity": "sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz", - "integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", - "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/type-utils": "5.59.11", - "@typescript-eslint/utils": "5.59.11", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", - "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", - "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", - "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.11", - "@typescript-eslint/utils": "5.59.11", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", - "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", - "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", - "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", - "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.59.11", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios-retry": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.4.1.tgz", - "integrity": "sha512-JGzNoglDHtHWIEvvAampB0P7jxQ/sT4COmW0FgSQkVg6o4KqNjNMBI6uFVOq517hkw/OAYYAG08ADtBlV8lvmQ==", - "dependencies": { - "is-retry-allowed": "^2.2.0" - }, - "peerDependencies": { - "axios": "0.x || 1.x" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001639", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001639.tgz", - "integrity": "sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.815", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz", - "integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz", - "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.38.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "27.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.3.tgz", - "integrity": "sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fetch-retry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-6.0.0.tgz", - "integrity": "sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/install": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", - "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-sdsl": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", - "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz", - "integrity": "sha512-Dp1C6SvSMYQI7YHq/y2l94uvI+59Eqbu1EpuKQHQ8p16txXRuRit5gH3Lnaagk2aXDIjg/Iru9pd05bnneKgdw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/promise-spawn", - "@npmcli/redact", - "@npmcli/run-script", - "@sigstore/tuf", - "abbrev", - "archy", - "cacache", - "chalk", - "ci-info", - "cli-columns", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "ms", - "node-gyp", - "nopt", - "normalize-package-data", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "semver", - "spdx-expression-parse", - "ssri", - "supports-color", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^7.5.3", - "@npmcli/config": "^8.3.3", - "@npmcli/fs": "^3.1.1", - "@npmcli/map-workspaces": "^3.0.6", - "@npmcli/package-json": "^5.1.1", - "@npmcli/promise-spawn": "^7.0.2", - "@npmcli/redact": "^2.0.0", - "@npmcli/run-script": "^8.1.0", - "@sigstore/tuf": "^2.3.4", - "abbrev": "^2.0.0", - "archy": "~1.0.0", - "cacache": "^18.0.3", - "chalk": "^5.3.0", - "ci-info": "^4.0.0", - "cli-columns": "^4.0.0", - "fastest-levenshtein": "^1.0.16", - "fs-minipass": "^3.0.3", - "glob": "^10.4.1", - "graceful-fs": "^4.2.11", - "hosted-git-info": "^7.0.2", - "ini": "^4.1.3", - "init-package-json": "^6.0.3", - "is-cidr": "^5.1.0", - "json-parse-even-better-errors": "^3.0.2", - "libnpmaccess": "^8.0.6", - "libnpmdiff": "^6.1.3", - "libnpmexec": "^8.1.2", - "libnpmfund": "^5.0.11", - "libnpmhook": "^10.0.5", - "libnpmorg": "^6.0.6", - "libnpmpack": "^7.0.3", - "libnpmpublish": "^9.0.9", - "libnpmsearch": "^7.0.6", - "libnpmteam": "^6.0.5", - "libnpmversion": "^6.0.3", - "make-fetch-happen": "^13.0.1", - "minimatch": "^9.0.4", - "minipass": "^7.1.1", - "minipass-pipeline": "^1.2.4", - "ms": "^2.1.2", - "node-gyp": "^10.1.0", - "nopt": "^7.2.1", - "normalize-package-data": "^6.0.1", - "npm-audit-report": "^5.0.0", - "npm-install-checks": "^6.3.0", - "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.1", - "npm-profile": "^10.0.0", - "npm-registry-fetch": "^17.0.1", - "npm-user-validate": "^2.0.1", - "p-map": "^4.0.0", - "pacote": "^18.0.6", - "parse-conflict-json": "^3.0.1", - "proc-log": "^4.2.0", - "qrcode-terminal": "^0.12.0", - "read": "^3.0.1", - "semver": "^7.6.2", - "spdx-expression-parse": "^4.0.0", - "ssri": "^10.0.6", - "supports-color": "^9.4.0", - "tar": "^6.2.1", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^3.0.0", - "validate-npm-package-name": "^5.0.1", - "which": "^4.0.0", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/agent": { - "version": "2.2.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "7.5.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^3.1.1", - "@npmcli/installed-package-contents": "^2.1.0", - "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^7.1.1", - "@npmcli/name-from-folder": "^2.0.0", - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.1.0", - "@npmcli/query": "^3.1.0", - "@npmcli/redact": "^2.0.0", - "@npmcli/run-script": "^8.1.0", - "bin-links": "^4.0.4", - "cacache": "^18.0.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^7.0.2", - "json-parse-even-better-errors": "^3.0.2", - "json-stringify-nice": "^1.1.4", - "lru-cache": "^10.2.2", - "minimatch": "^9.0.4", - "nopt": "^7.2.1", - "npm-install-checks": "^6.2.0", - "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.1", - "npm-registry-fetch": "^17.0.1", - "pacote": "^18.0.6", - "parse-conflict-json": "^3.0.0", - "proc-log": "^4.2.0", - "proggy": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^3.0.1", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^10.0.6", - "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "8.3.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^3.0.2", - "ci-info": "^4.0.0", - "ini": "^4.1.2", - "nopt": "^7.2.1", - "proc-log": "^4.2.0", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.5", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "3.1.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "5.0.7", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "3.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^2.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0", - "read-package-json-fast": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "7.1.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^18.0.0", - "json-parse-even-better-errors": "^3.0.0", - "pacote": "^18.0.0", - "proc-log": "^4.1.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "5.1.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^5.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^7.0.0", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "which": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "3.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/redact": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "8.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", - "which": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "2.3.2", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@sigstore/core": { - "version": "1.1.0", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.3.2", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@sigstore/sign": { - "version": "2.3.2", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "make-fetch-happen": "^13.0.1", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "2.3.4", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@sigstore/verify": { - "version": "1.2.1", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.1.0", - "@sigstore/protobuf-specs": "^0.3.2" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@tufjs/models": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/agent-base": { - "version": "7.1.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "6.2.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "4.0.4", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "read-cmd-shim": "^4.0.0", - "write-file-atomic": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.3.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/cacache": { - "version": "18.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/chalk": { - "version": "5.3.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ci-info": { - "version": "4.0.0", - "extraneous": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "4.1.1", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^5.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "6.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cross-spawn": { - "version": "7.0.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/diff": { - "version": "5.2.0", - "extraneous": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/eastasianwidth": { - "version": "0.2.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/exponential-backoff": { - "version": "3.1.1", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.16", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/npm/node_modules/foreground-child": { - "version": "3.1.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "3.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "10.4.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.11", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hasown": { - "version": "2.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "7.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "7.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.4", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "6.0.5", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ini": { - "version": "4.1.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "6.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^5.0.0", - "npm-package-arg": "^11.0.0", - "promzard": "^1.0.0", - "read": "^3.0.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/ip-address": { - "version": "9.0.5", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "5.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "5.1.0", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^4.1.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.13.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/jackspeak": { - "version": "3.1.2", - "extraneous": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/npm/node_modules/jsbn": { - "version": "1.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "engines": [ - "node >= 0.2.0" - ], - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "6.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.5.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "8.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "6.1.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^7.5.3", - "@npmcli/installed-package-contents": "^2.1.0", - "binary-extensions": "^2.3.0", - "diff": "^5.1.0", - "minimatch": "^9.0.4", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.6", - "tar": "^6.2.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "8.1.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^7.5.3", - "@npmcli/run-script": "^8.1.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.6", - "proc-log": "^4.2.0", - "read": "^3.0.1", - "read-package-json-fast": "^3.0.2", - "semver": "^7.3.7", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "5.0.11", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^7.5.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "10.0.5", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "6.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "7.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^7.5.3", - "@npmcli/run-script": "^8.1.0", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.6" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "9.0.9", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ci-info": "^4.0.0", - "normalize-package-data": "^6.0.1", - "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.1", - "proc-log": "^4.2.0", - "semver": "^7.3.7", - "sigstore": "^2.2.0", - "ssri": "^10.0.6" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "7.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^17.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "6.0.5", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "6.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^5.0.7", - "@npmcli/run-script": "^8.1.0", - "json-parse-even-better-errors": "^3.0.2", - "proc-log": "^4.2.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "10.2.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "13.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "9.0.4", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "7.1.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "3.0.5", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "1.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "10.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^4.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "7.2.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "6.0.1", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "5.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "6.3.0", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "11.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "8.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^6.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "9.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "10.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^17.0.1", - "proc-log": "^4.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "17.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^2.0.0", - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "BSD-2-Clause", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/pacote": { - "version": "18.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^5.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/package-json": "^5.1.0", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^8.0.0", - "cacache": "^18.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^11.0.0", - "npm-packlist": "^8.0.0", - "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^2.2.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/path-key": { - "version": "3.1.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/path-scurry": { - "version": "1.11.1", - "extraneous": true, - "inBundle": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/proc-log": { - "version": "4.2.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/proggy": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "1.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "^3.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "extraneous": true, - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/read": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "3.0.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/semver": { - "version": "7.6.2", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/shebang-command": { - "version": "2.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/shebang-regex": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "4.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/sigstore": { - "version": "2.3.1", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "@sigstore/sign": "^2.3.2", - "@sigstore/tuf": "^2.3.4", - "@sigstore/verify": "^1.2.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.8.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.7.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.2.0", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.5.0", - "extraneous": true, - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.18", - "extraneous": true, - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/sprintf-js": { - "version": "1.1.3", - "extraneous": true, - "inBundle": true, - "license": "BSD-3-Clause" - }, - "node_modules/npm/node_modules/ssri": { - "version": "10.0.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "9.4.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "6.2.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/tuf-js": { - "version": "2.2.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "3.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "extraneous": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "5.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "3.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/which": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/which/node_modules/isexe": { - "version": "3.1.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/npm/node_modules/wrap-ansi": { - "version": "8.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "extraneous": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "extraneous": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "5.0.1", - "extraneous": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "extraneous": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", - "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/ts-api-utils": { - "version": "0.0.46", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-0.0.46.tgz", - "integrity": "sha512-YKJeSx39n0mMk+hrpyHKyTgxA3s7Pz/j1cXYR+t8HcwwZupzOR5xDGKnOEw3gmLaUeFUQt3FJD39AH9Ajn/mdA==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index ff1a917..0000000 --- a/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "scrapfly-sdk", - "version": "0.6.0", - "description": "SDK for Scrapfly.io web scraping service", - "type": "module", - "types": "build/src/main.d.ts", - "main": "build/src/main.js", - "repository": { - "type": "git", - "url": "https://github.com/scrapfly/typescript-scrapfly" - }, - "bugs": "https://github.com/scrapfly/typescript-scrapfly/issues", - "homepage": "https://scrapfly.io/", - "keywords": [ - "web scraping", - "SDK", - "scrapfly", - "api" - ], - "files": [ - "build/src" - ], - "engines": { - "node": ">= 18 <19" - }, - "devDependencies": { - "@types/node": "~18", - "@typescript-eslint/eslint-plugin": "~5.59", - "@typescript-eslint/parser": "~5.59", - "eslint": "~8.38", - "eslint-config-prettier": "~8.8", - "eslint-plugin-jest": "~27.2", - "jest": "~29.5", - "prettier": "~2.8", - "rimraf": "~5.0", - "ts-api-utils": "~0.0.44", - "ts-jest": "~29.1", - "typescript": "^5.1.6" - }, - "scripts": { - "clean": "rimraf coverage build tmp", - "prebuild": "npm run lint", - "build": "tsc -p tsconfig.json", - "build:watch": "tsc -w -p tsconfig.json", - "build:release": "npm run clean && tsc -p tsconfig.release.json", - "lint": "eslint . --ext .ts --ext .mts", - "test": "jest --coverage", - "prettier": "prettier --config .prettierrc --write .", - "test:watch": "jest --watch" - }, - "author": "Bernardas Alisauskas ", - "license": "BSD", - "dependencies": { - "@types/cheerio": "^0.22.31", - "fetch-retry": "^6.0.0", - "cheerio": "^1.0.0-rc.12", - "tslib": "~2.5" - }, - "volta": { - "node": "18.17.0" - } -} diff --git a/publish-jsr.sh b/publish-jsr.sh new file mode 100755 index 0000000..a0695d2 --- /dev/null +++ b/publish-jsr.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Clean up any existing build directory +rm -rf build + +# Create a fresh build directory +mkdir build + +# Copy necessary files to the build directory +cp -R src build/ +cp -R __tests__ build/ +cp deno.json build/ +cp README.md build/ +cp LICENSE build/ + +# Change to the build directory +cd build + +# Publish the package +deno task test +rm -r __tests__ +deno publish --dry-run --allow-dirty diff --git a/src/client.ts b/src/client.ts index 95d8159..ee27199 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,390 +1,368 @@ -import fs from 'fs'; -import path from 'path'; -import * as errors from './errors.js'; -import { ScrapeConfig } from './scrapeconfig.js'; -import { ScreenshotConfig } from './screenshotconfig.js'; -import { ExtractionConfig } from './extractionconfig.js'; -import { - ConfigData, - ContextData, - ResultData, - ScrapeResult, - AccountData, - ScreenshotResult, - ExtractionResult, -} from './result.js'; -import fetchRetry from 'fetch-retry'; -import { log } from './logger.js'; - -function fetchTimeout(resource: Request, options: RequestInit = {}): Promise { - const timeout = 160000; // 160 seconds - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - const signal = controller.signal; - - // Add the signal to the options object - options.signal = signal; - - return fetch(resource, options) - .then((response) => { - clearTimeout(timeoutId); - return response; - }) - .catch((error) => { - clearTimeout(timeoutId); - throw error; - }); -} +import { path } from './deps.ts'; +import { fetchRetry } from './utils.ts'; +import * as errors from './errors.ts'; +import type { ScrapeConfig } from './scrapeconfig.ts'; +import type { ScreenshotConfig } from './screenshotconfig.ts'; +import type { ExtractionConfig } from './extractionconfig.ts'; +import { type AccountData, ExtractionResult, ScrapeResult, ScreenshotResult } from './result.ts'; +import { log } from './logger.ts'; +import type { Rec } from './types.ts'; export class ScrapflyClient { - public HOST = 'https://api.scrapfly.io'; - private key: string; - private ua: string; + public HOST = 'https://api.scrapfly.io'; + private key: string; + private ua: string; + fetch = fetchRetry; - constructor(options: { key: string }) { - if (typeof options.key !== 'string' || options.key.trim() === '') { - throw new errors.BadApiKeyError('Invalid key. Key must be a non-empty string'); - } - this.key = options.key; - this.ua = 'Typescript Scrapfly SDK'; + constructor(options: { key: string }) { + if (typeof options.key !== 'string' || options.key.trim() === '') { + throw new errors.BadApiKeyError('Invalid key. Key must be a non-empty string'); } + this.key = options.key; + this.ua = 'Typescript Scrapfly SDK'; + } - fetch = fetchRetry(fetchTimeout, { - retryDelay: 1000, - retryOn: function (attempt, error, response) { - const maxRetries = 3; - // retry on any network errors or 5xx status codes - if (attempt <= maxRetries && (error !== null || (response && response.status >= 500))) { - return true; - } - return false; - }, - }); - - /** - * Raise appropriate error for given response and scrape result - */ - errResult(response: Response, result: ScrapeResult): errors.ScrapflyError { - const error = result.result.error; - const message = error.message ?? ''; - const args = { - code: result.result.status, - http_status_code: result.result.status_code, - is_retryable: error.retryable ?? false, - api_response: result, - resource: result.result.status ? result.result.status.split('::')[1] : null, - retry_delay: error.retryable ? 5 : response.headers.get('X-Retry') ?? 5, - retry_times: 3, - documentation_url: error.doc_url ?? 'https://scrapfly.io/docs/scrape-api/errors#api', - }; - const resourceErrMap = { - SCRAPE: errors.ScrapflyScrapeError, - WEBHOOK: errors.ScrapflyWebhookError, - PROXY: errors.ScrapflyProxyError, - SCHEDULE: errors.ScrapflyScheduleError, - ASP: errors.ScrapflyAspError, - SESSION: errors.ScrapflySessionError, - }; - const httpStatusErrMap = { - 401: errors.BadApiKeyError, - 429: errors.TooManyRequests, - }; - if (result.result.success === true) { - if (args.http_status_code >= 500) { - return new errors.ApiHttpServerError(message, args); - } - if (httpStatusErrMap[args.http_status_code]) { - return new httpStatusErrMap[args.http_status_code](message, args); - } - if (resourceErrMap[args.resource]) { - return new resourceErrMap[args.resource](message, args); - } - return new errors.ApiHttpClientError(message, args); - } else { - if (args.code === 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE') { - if (args.http_status_code >= 500) { - return new errors.UpstreamHttpServerError(message, args); - } - if (args.http_status_code >= 400) { - return new errors.UpstreamHttpClientError(message, args); - } - } - if (resourceErrMap[args.resource]) { - return new resourceErrMap[args.resource](message, args); - } - throw new errors.ScrapflyError(message, args); + /** + * Raise appropriate error for given response and scrape result + */ + errResult(response: Response, result: ScrapeResult): errors.ScrapflyError { + const error = result.result.error; + const message = error?.message ?? ''; + const args = { + code: result.result.status, + http_status_code: result.result.status_code, + is_retryable: error?.retryable ?? false, + api_response: result, + resource: result.result.status ? result.result.status.split('::')[1] : null, + retry_delay: error?.retryable ? 5 : response.headers.get('X-Retry') ?? 5, + retry_times: 3, + documentation_url: error?.doc_url ?? 'https://scrapfly.io/docs/scrape-api/errors#api', + }; + if (result.result.success === true) { + switch (args.http_status_code) { + case 500: + return new errors.ApiHttpServerError(message, args); + case 401: + return new errors.BadApiKeyError(message, args); + case 429: + return new errors.TooManyRequests(message, args); + } + switch (args.resource) { + case 'SCRAPE': + return new errors.ScrapflyScrapeError(message, args); + case 'WEBHOOK': + return new errors.ScrapflyWebhookError(message, args); + case 'PROXY': + return new errors.ScrapflyProxyError(message, args); + case 'SCHEDULE': + return new errors.ScrapflyScheduleError(message, args); + case 'ASP': + return new errors.ScrapflyAspError(message, args); + case 'SESSION': + return new errors.ScrapflySessionError(message, args); + } + if (args.resource) { + return new errors.ApiHttpClientError(message, args); + } + } else { + if (args.code === 'ERR::SCRAPE::BAD_UPSTREAM_RESPONSE') { + if (args.http_status_code >= 400 && args.http_status_code < 500) { + return new errors.UpstreamHttpClientError(message, args); + } + if (args.http_status_code >= 500 && args.http_status_code < 600) { + return new errors.UpstreamHttpServerError(message, args); } + } + switch (args.resource) { + case 'SCRAPE': + return new errors.ScrapflyScrapeError(message, args); + case 'WEBHOOK': + return new errors.ScrapflyWebhookError(message, args); + case 'PROXY': + return new errors.ScrapflyProxyError(message, args); + case 'SCHEDULE': + return new errors.ScrapflyScheduleError(message, args); + case 'ASP': + return new errors.ScrapflyAspError(message, args); + case 'SESSION': + return new errors.ScrapflySessionError(message, args); + } } + return new errors.ScrapflyError(message, args); + } - /** - * Turn scrapfly API response to ScrapeResult or raise one of ScrapflyError - */ - async handleResponse(response: Response, data): Promise { - data = data as { - config: ConfigData; - context: ContextData; - result: ResultData; - uuid: string; - }; - const result = new ScrapeResult(data); - // success - log.debug('scrape log url: ', result.result.log_url); - if (result.result.status === 'DONE' && result.result.success === true) { - return result; - } - // something went wrong - throw this.errResult(response, result); + /** + * Turn scrapfly API response to ScrapeResult or raise one of ScrapflyError + */ + handleResponse(response: Response, result: ScrapeResult): ScrapeResult { + // success + log.debug('scrape log url: ', result.result.log_url); + if (result.result.status === 'DONE' && result.result.success === true) { + return result; } + // something went wrong + throw this.errResult(response, result); + } - /** - * Retrieve Scrapfly account details - */ - async account(): Promise { - log.debug('retrieving account info'); - let response; - try { - const url = new URL(this.HOST + '/account'); - const params = { key: this.key }; - url.search = new URLSearchParams(params).toString(); - response = await this.fetch( - new Request(url.toString(), { - method: 'GET', - headers: { - 'user-agent': this.ua, - 'accept-encoding': 'gzip, deflate, br', - accept: 'application/json', - }, - }), - ); - } catch (e) { - log.error('error', e); - throw e; - } - const data = await response.json(); - if ('error_id' in data || Object.keys(data).length === 0) { - if (data.http_code == 401 || response.status == 401) { - throw new errors.BadApiKeyError(JSON.stringify(data)); - } - throw new errors.ApiHttpClientError(JSON.stringify(data)); - } - return data; + /** + * Retrieve Scrapfly account details + */ + async account(): Promise { + log.debug('retrieving account info'); + let response: Response; + try { + const url = new URL(this.HOST + '/account'); + const params = { key: this.key }; + url.search = new URLSearchParams(params).toString(); + response = await this.fetch( + new Request(url.toString(), { + method: 'GET', + headers: { + 'user-agent': this.ua, + 'accept-encoding': 'gzip, deflate, br', + accept: 'application/json', + }, + }), + ); + } catch (e) { + log.error('error', e); + throw e; } + const data: Rec = await response.json() as Rec; + if ('error_id' in data || Object.keys(data).length === 0) { + if (data.http_code == 401 || response.status == 401) { + throw new errors.BadApiKeyError(JSON.stringify(data)); + } + throw new errors.ApiHttpClientError(JSON.stringify(data)); + } + return data as AccountData; + } - /** - * Issue a single scrape command from a given scrape configuration - */ - async scrape(config: ScrapeConfig): Promise { - log.debug('scraping', { method: config.method, url: config.url }); - let response; - try { - const url = new URL(this.HOST + '/scrape'); - const params = config.toApiParams({ key: this.key }); - url.search = new URLSearchParams(params).toString(); - response = await this.fetch( - new Request(url.toString(), { - method: config.method, - headers: { - 'user-agent': this.ua, - 'content-type': config.method === 'POST' ? config.headers['content-type'] : 'application/json', - 'accept-encoding': 'gzip, deflate, br', - accept: 'application/json', - }, - body: config.body, - }), - ); - } catch (e) { - log.error('error', e); - e.scrapeConfig = config; - throw e; - } - const data = await response.json(); - if ('error_id' in data || Object.keys(data).length === 0) { - if (data.http_code == 401 || response.status == 401) { - throw new errors.BadApiKeyError(JSON.stringify(data)); - } - throw new errors.ApiHttpClientError(JSON.stringify(data)); - } - const result = await this.handleResponse(response, data); - return result; + /** + * Issue a single scrape command from a given scrape configuration + */ + async scrape(config: ScrapeConfig): Promise { + log.debug('scraping', { method: config.method, url: config.url }); + let response; + try { + const url = new URL(this.HOST + '/scrape'); + const params = config.toApiParams({ key: this.key }); + url.search = new URLSearchParams(params).toString(); + response = await this.fetch( + new Request(url.toString(), { + method: config.method, + headers: { + 'user-agent': this.ua, + 'content-type': config.method === 'POST' + ? config.headers?.['content-type'] ?? 'application/json' + : 'application/json', + 'accept-encoding': 'gzip, deflate, br', + accept: 'application/json', + }, + body: config.body, + }), + ); + } catch (e) { + log.error('error', e); + e.scrapeConfig = config; + throw e; + } + const data: Rec = await response.json() as Rec; + if ('error_id' in data || Object.keys(data).length === 0) { + if (data.http_code == 401 || response.status == 401) { + throw new errors.BadApiKeyError(JSON.stringify(data)); + } + throw new errors.ApiHttpClientError(JSON.stringify(data)); } + const result = this.handleResponse( + response, + new ScrapeResult({ + config: data.config, + context: data.context, + result: data.result, + uuid: data.uuid, + }), + ); + return result; + } - /** - Concurrently scrape multiple configs - This is a async generator call it like this: - - const results = []; - const errors = []; - for await (const resultOrError of client.concurrentScrape(configs)) { - if (resultOrError instanceof Error) { - errors.push(resultOrError); - } else { - results.push(resultOrError); - } + /** + Concurrently scrape multiple configs + This is a async generator call it like this: + + const results = []; + const errors = []; + for await (const resultOrError of client.concurrentScrape(configs)) { + if (resultOrError instanceof Error) { + errors.push(resultOrError); + } else { + results.push(resultOrError); } - - @param concurrencyLimit: if not set it will be taken from your account info - */ - async *concurrentScrape( - configs: ScrapeConfig[], - concurrencyLimit?: number, - ): AsyncGenerator { - if (concurrencyLimit === undefined) { - const account = await this.account(); - concurrencyLimit = account.subscription.usage.scrape.concurrent_limit; - log.info(`concurrency not provided - setting it to ${concurrencyLimit} from account info`); - } - const activePromises = new Set>(); - const configsIterator = configs[Symbol.iterator](); + } - // Helper function to start a new scrape and add it to activePromises - const startNewScrape = () => { - const { value: config, done } = configsIterator.next(); - if (done) return; // No more configs + @param concurrencyLimit: if not set it will be taken from your account info + */ + async *concurrentScrape( + configs: ScrapeConfig[], + concurrencyLimit?: number, + ): AsyncGenerator { + if (concurrencyLimit === undefined) { + const account = await this.account(); + concurrencyLimit = account.subscription.usage.scrape.concurrent_limit; + log.info(`concurrency not provided - setting it to ${concurrencyLimit} from account info`); + } + const activePromises = new Set>(); + const configsIterator = configs[Symbol.iterator](); - const promise = this.scrape(config).catch((error) => error); // Catch errors and return them - activePromises.add(promise); + // Helper function to start a new scrape and add it to activePromises + const startNewScrape = () => { + const { value: config, done } = configsIterator.next(); + if (done) return; // No more configs - promise.finally(() => { - activePromises.delete(promise); - // After each scrape, start a new one if there are remaining configs - startNewScrape(); - }); - }; + const promise = this.scrape(config).catch((error) => error); // Catch errors and return them + activePromises.add(promise); - // Initially start as many scrapes as the concurrency limit - for (let i = 0; i < concurrencyLimit; i++) { - startNewScrape(); - } + promise.finally(() => { + activePromises.delete(promise); + // After each scrape, start a new one if there are remaining configs + startNewScrape(); + }); + }; - // As each scrape finishes, yield the result or error and start a new one if there are remaining configs - while (activePromises.size > 0) { - log.debug(`concurrently scraping ${activePromises.size}/${configs.length}}`); - const resultOrError = await Promise.race(activePromises); - yield resultOrError; - } + // Initially start as many scrapes as the concurrency limit + for (let i = 0; i < concurrencyLimit; i++) { + startNewScrape(); } - /** - * Save screenshot response to a file - */ - async saveScreenshot(result: ScreenshotResult, name: string, savePath?: string): Promise { - if (!result.image) { - throw new Error('Screenshot binary does not exist'); - } + // As each scrape finishes, yield the result or error and start a new one if there are remaining configs + while (activePromises.size > 0) { + log.debug(`concurrently scraping ${activePromises.size}/${configs.length}}`); + const resultOrError = await Promise.race(activePromises); + yield resultOrError; + } + } - const extension_name = result.metadata.extension_name; - let file_path; + /** + * Save screenshot response to a file + */ + async saveScreenshot(result: ScreenshotResult, name: string, savePath?: string): Promise { + if (!result.image) { + throw new Error('Screenshot binary does not exist'); + } - if (savePath) { - fs.mkdirSync(savePath, { recursive: true }); - file_path = path.join(savePath, `${name}.${extension_name}`); - } else { - file_path = `${name}.${extension_name}`; - } + const extension_name = result.metadata.extension_name; + let file_path; - const content = Buffer.from(result.image); - fs.writeFileSync(file_path, content, 'binary'); + if (savePath) { + Deno.mkdir(savePath, { recursive: true }); + file_path = path.join(savePath, `${name}.${extension_name}`); + } else { + file_path = `${name}.${extension_name}`; } - /** - * Turn scrapfly screenshot API response to ScreenshotResult or raise one of ScrapflyError - */ - async handleScreenshotResponse(response: Response): Promise { - if (response.headers.get('content-encoding') != 'gzip') { - const data = (await response.json()) as any; - if (data.http_code == 401 || response.status == 401) { - throw new errors.BadApiKeyError(JSON.stringify(data)); - } - if ('error_id' in data) { - throw new errors.ScreenshotApiError(JSON.stringify(data)); - } - } - if (!response.ok) { - throw new errors.ApiHttpClientError(JSON.stringify(await response.json())); - } - const data = await response.arrayBuffer(); - const result = new ScreenshotResult(response, data); - return result; + const content = new Uint8Array(result.image); + // Use Deno's write file method + await Deno.writeFile(file_path, content); + } + + /** + * Turn scrapfly screenshot API response to ScreenshotResult or raise one of ScrapflyError + */ + async handleScreenshotResponse(response: Response): Promise { + if (response.headers.get('content-type') === 'application/json') { + const data: Rec = (await response.json()) as Rec; + if (data.http_code == 401 || response.status == 401) { + throw new errors.BadApiKeyError(JSON.stringify(data)); + } + if ('error_id' in data) { + throw new errors.ScreenshotApiError(JSON.stringify(data)); + } + } + if (!response.ok) { + throw new errors.ScreenshotApiError(JSON.stringify(await response.json())); } + const data = await response.arrayBuffer(); + const result = new ScreenshotResult(response, data); + return result; + } - /** - * Take a screenshot - */ - async screenshot(config: ScreenshotConfig): Promise { - log.debug('screenshoting', { url: config.url }); - let response; - try { - const url = new URL(this.HOST + '/screenshot'); - const params = config.toApiParams({ key: this.key }); - url.search = new URLSearchParams(params).toString(); - response = await this.fetch( - new Request(url.toString(), { - method: 'GET', - headers: { - 'user-agent': this.ua, - 'accept-encoding': 'gzip, deflate, br', - accept: 'application/json', - }, - }), - ); - } catch (e) { - log.error('error', e); - throw e; - } - const result = await this.handleScreenshotResponse(response); - return result; + /** + * Take a screenshot + */ + async screenshot(config: ScreenshotConfig): Promise { + let response; + try { + const url = new URL(this.HOST + '/screenshot'); + const params = config.toApiParams({ key: this.key }); + url.search = new URLSearchParams(params).toString(); + const req = new Request(url.toString(), { + method: 'GET', + headers: { + 'user-agent': this.ua, + 'accept-encoding': 'gzip, deflate, br', + accept: 'application/json', + }, + }); + response = await this.fetch(req); + } catch (e) { + log.error('error', e); + throw e; } + const result = await this.handleScreenshotResponse(response); + return result; + } - /** - * Turn scrapfly Extraction API response to ExtractionResult or raise one of ScrapflyError - */ - async handleExtractionResponse(response: Response): Promise { - const data = (await response.json()) as any; - if ('error_id' in data) { - if (data.http_code == 401 || response.status == 401) { - throw new errors.BadApiKeyError(JSON.stringify(data)); - } - throw new errors.ExtractionApiError(JSON.stringify(data)); - } - if (!response.ok) { - throw new errors.ApiHttpClientError(JSON.stringify(await response.json())); - } - const result = new ExtractionResult(data); - return result; + /** + * Turn scrapfly Extraction API response to ExtractionResult or raise one of ScrapflyError + */ + async handleExtractionResponse(response: Response): Promise { + const data: Rec = (await response.json()) as Rec; + if ('error_id' in data) { + if (data.http_code == 401 || response.status == 401) { + throw new errors.BadApiKeyError(JSON.stringify(data)); + } + throw new errors.ExtractionApiError(JSON.stringify(data)); + } + if (!response.ok) { + throw new errors.ApiHttpClientError(JSON.stringify(await response.json())); } + const result = new ExtractionResult({ + data: data.data, + content_type: data.content_type, + }); + return result; + } - /** - * Extract structured data from a web page - */ - async extract(config: ExtractionConfig): Promise { - log.debug('extacting data from', { content_type: config.content_type }); - let response; - try { - const url = new URL(this.HOST + '/extraction'); - const params = await config.toApiParams({ key: this.key }); - url.search = new URLSearchParams(params).toString(); - const headers: Record = { - 'user-agent': this.ua, - 'accept-encoding': 'gzip, deflate, br', - 'content-type': config.content_type, - accept: 'application/json', - }; - if (config.document_compression_format && config.document_compression_format) { - headers['content-encoding'] = config.document_compression_format; - } - response = await this.fetch( - new Request(url.toString(), { - method: 'POST', - headers: headers, - body: config.body, - }), - ); - } catch (e) { - log.error('error', e); - throw e; - } - const result = await this.handleExtractionResponse(response); - return result; + /** + * Extract structured data from a web page + */ + async extract(config: ExtractionConfig): Promise { + log.debug('extacting data from', { content_type: config.content_type }); + let response; + try { + const url = new URL(this.HOST + '/extraction'); + const params = await config.toApiParams({ key: this.key }); + url.search = new URLSearchParams(params).toString(); + const headers: Record = { + 'user-agent': this.ua, + 'accept-encoding': 'gzip, deflate, br', + 'content-type': config.content_type, + 'accept': 'application/json', + }; + if (config.document_compression_format && config.document_compression_format) { + headers['content-encoding'] = config.document_compression_format; + } + const req = new Request(url.toString(), { + method: 'POST', + headers: headers, + body: config.body, + }); + response = await this.fetch(req); + } catch (e) { + log.error('error', e); + throw e; } + const result = await this.handleExtractionResponse(response); + return result; + } } diff --git a/src/deps.ts b/src/deps.ts new file mode 100644 index 0000000..cfa8b92 --- /dev/null +++ b/src/deps.ts @@ -0,0 +1,4 @@ +import * as cheerio from "npm:cheerio@1.0.0-rc.12"; +import * as path from 'jsr:@std/path@1.0.1'; + +export { cheerio, path}; \ No newline at end of file diff --git a/src/errors.ts b/src/errors.ts index 9031b7b..4779996 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,10 +1,10 @@ export class ScrapflyError extends Error { - args: Record; + args: Record; - constructor(message: string, args?: Record) { - super(message); - this.args = args; - } + constructor(message: string, args: Record = {}) { + super(message); + this.args = args; + } } // raised when scrape config is invalid diff --git a/src/extractionconfig.ts b/src/extractionconfig.ts index a2f29e5..d5d1be2 100644 --- a/src/extractionconfig.ts +++ b/src/extractionconfig.ts @@ -1,13 +1,10 @@ -import { gzip } from 'zlib'; -import { ExtractionConfigError } from './errors.js'; -import { errors } from './main.js'; -import { urlsafe_b64encode } from './utils.js'; -import { promisify } from 'util'; - -const gzipPromise = promisify(gzip); +import * as errors from './errors.ts'; +import { urlsafe_b64encode } from './utils.ts'; +import { gzipSync } from 'node:zlib'; +import { Buffer } from 'node:buffer'; export enum CompressionFormat { - /** + /** Document compression format. Attributes: @@ -16,108 +13,124 @@ export enum CompressionFormat { DEFLATE: deflate. """ */ - GZIP = 'gzip', - ZSTD = 'zstd', - DEFLATE = 'deflate', + GZIP = 'gzip', + ZSTD = 'zstd', + DEFLATE = 'deflate', } +type ExtractionConfigOptions = { + body: string; + content_type: string; + url?: string; + charset?: string; + template?: string; // saved template name + ephemeral_template?: object; // ephemeraly declared json template + extraction_prompt?: string; + extraction_model?: string; + is_document_compressed?: boolean; + document_compression_format?: CompressionFormat; + webhook?: string; +}; + export class ExtractionConfig { - body: string | Buffer; - content_type: string; - url?: string = null; - charset?: string = null; - template?: string; // saved template name - ephemeral_template?: object; // ephemeraly declared json template - extraction_prompt?: string = null; - extraction_model?: string = null; - is_document_compressed?: boolean = null; - document_compression_format?: CompressionFormat = null; - webhook?: string = null; - - constructor(options: { - body: string; - content_type: string; - url?: string; - charset?: string; - template?: string; // saved template name - ephemeral_template?: object; // ephemeraly declared json template - extraction_prompt?: string; - extraction_model?: string; - is_document_compressed?: boolean; - document_compression_format?: CompressionFormat; - webhook?: string; - }) { - this.body = options.body; - this.content_type = options.content_type; - this.url = options.url; - this.charset = options.charset; - this.template = options.template; - this.ephemeral_template = options.ephemeral_template; - this.extraction_prompt = options.extraction_prompt; - this.extraction_model = options.extraction_model; - this.is_document_compressed = options.is_document_compressed; - this.document_compression_format = options.document_compression_format; - this.webhook = options.webhook; + body: string | Uint8Array; + content_type: string; + url?: string; + charset?: string; + template?: string; // saved template name + ephemeral_template?: object; // ephemeraly declared json template + extraction_prompt?: string; + extraction_model?: string; + is_document_compressed?: boolean; + document_compression_format?: CompressionFormat; + webhook?: string; + + constructor(options: ExtractionConfigOptions) { + this.validateOptions(options); + this.body = options.body; + this.content_type = options.content_type; + this.url = options.url ?? this.url; + this.charset = options.charset ?? this.charset; + this.template = options.template ?? this.template; + this.ephemeral_template = options.ephemeral_template ?? this.ephemeral_template; + this.extraction_prompt = options.extraction_prompt ?? this.extraction_prompt; + this.extraction_model = options.extraction_model ?? this.extraction_model; + this.is_document_compressed = options.is_document_compressed ?? this.is_document_compressed; + this.document_compression_format = options.document_compression_format ?? this.document_compression_format; + this.webhook = options.webhook ?? this.webhook; + } + + private validateOptions(options: Partial) { + const validKeys = new Set(Object.keys(this) as Array); + for (const key in options) { + if (!validKeys.has(key as keyof ExtractionConfig)) { + throw new errors.ExtractionConfigError(`Invalid option provided: ${key}`); + } } + } - async toApiParams(options: { key: string }): Promise> { - const params: Record = { - key: options.key, - }; - // params.body = this.body; - params.content_type = this.content_type; + toApiParams(options: { key: string }): Record { + const params: Record = { + key: options.key, + }; + // params.body = this.body; + params.content_type = this.content_type; - if (this.url) { - params.url = encodeURI(this.url); - } - - if (this.charset) { - params.charset = this.charset; - } + if (this.url) { + params.url = encodeURI(this.url); + } - if (this.template && this.ephemeral_template) { - throw new ExtractionConfigError( - 'You cannot pass both parameters template and ephemeral_template. You must choose', - ); - } + if (this.charset) { + params.charset = this.charset; + } - if (this.template) { - params.extraction_template = this.template; - } + if (this.template && this.ephemeral_template) { + throw new errors.ExtractionConfigError( + 'You cannot pass both parameters template and ephemeral_template. You must choose', + ); + } - if (this.ephemeral_template) { - params.extraction_template = 'ephemeral:' + urlsafe_b64encode(JSON.stringify(this.ephemeral_template)); - } + if (this.template) { + params.extraction_template = this.template; + } - if (this.extraction_prompt) { - params.extraction_prompt = this.extraction_prompt; - } + if (this.ephemeral_template) { + params.extraction_template = 'ephemeral:' + urlsafe_b64encode(JSON.stringify(this.ephemeral_template)); + } - if (this.extraction_model) { - params.extraction_model = this.extraction_model; - } + if (this.extraction_prompt) { + params.extraction_prompt = this.extraction_prompt; + } - if (this.document_compression_format) { - if (this.is_document_compressed == null) { - throw new errors.ExtractionConfigError( - 'When declaring compression format, your must declare the is_document_compressed parameter to compress the document or skip it.', - ); - } - if (this.is_document_compressed == false) { - if (this.document_compression_format == CompressionFormat.GZIP) { - this.body = await gzipPromise(Buffer.from(this.body as string, 'utf-8')); - } else { - throw new errors.ExtractionConfigError( - `Auto compression for ${this.document_compression_format} format isn't available. You can manually compress to ${this.document_compression_format} or choose the gzip format for auto compression`, - ); - } - } - } + if (this.extraction_model) { + params.extraction_model = this.extraction_model; + } - if (this.webhook) { - params.webhook_name = this.webhook; + if (this.document_compression_format) { + if (this.is_document_compressed === undefined) { + throw new errors.ExtractionConfigError( + `When declaring compression format, your must declare the ` + + `is_document_compressed parameter to compress the document or skip it.`, + ); + } + if (this.is_document_compressed === false) { + if (this.document_compression_format === CompressionFormat.GZIP) { + const compressed = gzipSync(Buffer.from(this.body as string, 'utf-8')); + this.body = new Uint8Array(compressed); + } else { + throw new errors.ExtractionConfigError( + `Auto compression for ${this.document_compression_format} format isn't available. ` + + `You can manually compress to ${this.document_compression_format}` + + `or choose the gzip format for auto compression`, + ); } + } + } - return params; + if (this.webhook) { + params.webhook_name = this.webhook; } + + return params; + } } diff --git a/src/logger.ts b/src/logger.ts index 0d685a3..69e9e10 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,51 +1,51 @@ enum LogLevel { - DEBUG = 'DEBUG', - INFO = 'INFO', - WARN = 'WARN', - ERROR = 'ERROR', + DEBUG = 'DEBUG', + INFO = 'INFO', + WARN = 'WARN', + ERROR = 'ERROR', } class Logger { - private level: LogLevel; - public name: string; + private level: LogLevel; + public name: string; - constructor(name: string, level: LogLevel = LogLevel.INFO) { - this.name = name; - this.level = level; - } + constructor(name: string, level: LogLevel = LogLevel.INFO) { + this.name = name; + this.level = level; + } - debug(...args: any[]): void { - if (this.level <= LogLevel.DEBUG) { - console.log(...args); - } + debug(...args: any[]): void { + if (this.level <= LogLevel.DEBUG) { + console.log(...args); } + } - info(...args: any[]): void { - if (this.level <= LogLevel.INFO) { - console.info(...args); - } + info(...args: any[]): void { + if (this.level <= LogLevel.INFO) { + console.info(...args); } + } - warn(...args: any[]): void { - if (this.level <= LogLevel.WARN) { - console.warn(...args); - } + warn(...args: any[]): void { + if (this.level <= LogLevel.WARN) { + console.warn(...args); } + } - error(...args: any[]): void { - if (this.level <= LogLevel.ERROR) { - console.error(...args); - } + error(...args: any[]): void { + if (this.level <= LogLevel.ERROR) { + console.error(...args); } + } - setLevel(level: string): void { - if (Object.values(LogLevel).includes(level as LogLevel)) { - this.level = level as LogLevel; - } else { - console.error(`Invalid log level: ${level}`); - } + setLevel(level: string): void { + if (Object.values(LogLevel).includes(level as LogLevel)) { + this.level = level as LogLevel; + } else { + console.error(`Invalid log level: ${level}`); } + } } export default Logger; -export const log = new Logger('scrapfly'); +export const log: Logger = new Logger('scrapfly'); diff --git a/src/main.ts b/src/main.ts index fa50873..df230c2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,16 +1,15 @@ -export { ScrapflyClient } from './client.js'; -export { ScrapeConfig, ScreenshotFlags, Format as ScrapeFormat } from './scrapeconfig.js'; -export { ScreenshotConfig, Format as ScreenshotFormat, Options as ScreenshotOptions } from './screenshotconfig.js'; -export { ExtractionConfig } from './extractionconfig.js'; -export * as errors from './errors.js'; +export { ScrapflyClient } from './client.ts'; export { - AccountData, - ConfigData, - ResultData, - ScrapeResult, - ContextData, - ScreenshotMetadata, - ScreenshotResult, - ExtractionResult -} from './result.js'; -export { log } from './logger.js'; + Format as ScrapeFormat, + FormatOption as ScrapeFormatOptions, + ScrapeConfig, + ScreenshotFlags, +} from './scrapeconfig.ts'; +export { Format as ScreenshotFormat, Options as ScreenshotOptions, ScreenshotConfig } from './screenshotconfig.ts'; +export { ExtractionConfig } from './extractionconfig.ts'; +export * as errors from './errors.ts'; + +export { ExtractionResult, ScrapeResult, ScreenshotResult } from './result.ts'; + +export type { AccountData, ConfigData, ContextData, ResultData, ScreenshotMetadata } from './result.ts'; +export { log } from './logger.ts'; diff --git a/src/result.ts b/src/result.ts index 76763b5..b0919d2 100644 --- a/src/result.ts +++ b/src/result.ts @@ -1,317 +1,320 @@ -import { Rec, HttpMethod } from './types.js'; -import * as errors from './errors.js'; -import * as cheerio from 'cheerio'; +import type { HttpMethod, Rec } from './types.ts'; +import * as errors from './errors.ts'; +import { cheerio } from './deps.ts'; export type ConfigData = { - url: string; - retry: boolean; - method: HttpMethod; - country?: string; - render_js: boolean; - cache: boolean; - cache_clear: boolean; - ssl: boolean; - dns: boolean; - asp: boolean; - debug: boolean; - raise_on_upstream_error: boolean; - cache_ttl: number; - proxy_pool: string; - session?: string; - tags: Set; - correlation_id?: string; - cookies: Record; - body?: string; - data?: Rec; - headers: Rec; - js?: string; - rendering_wait?: number; - wait_for_selector?: string; - session_sticky_proxy: boolean; - screenshots?: Rec; - webhook?: string; - timeout?: number; // in milliseconds - js_scenario?: Rec; - extract?: Rec; - lang?: string[]; - os?: string; - auto_scroll?: boolean; + url: string; + retry: boolean; + method: HttpMethod; + country?: string; + render_js: boolean; + cache: boolean; + cache_clear: boolean; + ssl: boolean; + dns: boolean; + asp: boolean; + debug: boolean; + raise_on_upstream_error: boolean; + cache_ttl: number; + proxy_pool: string; + session?: string; + tags: Set; + correlation_id?: string; + cookies: Record; + body?: string; + data?: Rec; + headers: Rec; + js?: string; + rendering_wait?: number; + wait_for_selector?: string; + session_sticky_proxy: boolean; + screenshots?: Rec; + webhook?: string; + timeout?: number; // in milliseconds + js_scenario?: Rec; + extract?: Rec; + lang?: string[]; + os?: string; + auto_scroll?: boolean; }; export type ContextData = { - asp: boolean; - bandwidth_consumed: number; - cache: { - state: string; - entry?: string; - }; - cookies: Record; - cost: { - details: Array<{ amount: number; code: string; description: string }>; - total: number; - }; - created_at: string; - debug: { - response_url: string; - screenshot_url?: string; - }; - env: string; - fingerprint: string; - headers: Record; - is_xml_http_request: boolean; - job?: string; - lang: Array; - os: { - distribution: string; - name: string; - type: string; - version: string; - }; - project: string; - proxy: { - country: string; - identity: string; - network: string; - pool: string; - }; - redirects: Array; - retry: number; - schedule?: string; - session?: string; - spider?: any; - throttler?: any; - uri: { - base_url: string; - fragment?: string; - host: string; - params?: Rec; - port: number; - query?: string; - root_domain: string; - scheme: string; - }; - url: string; - webhook?: string; + asp: boolean; + bandwidth_consumed: number; + cache: { + state: string; + entry?: string; + }; + cookies: Record; + cost: { + details: Array<{ amount: number; code: string; description: string }>; + total: number; + }; + created_at: string; + debug: { + response_url: string; + screenshot_url?: string; + }; + env: string; + fingerprint: string; + headers: Record; + is_xml_http_request: boolean; + job?: string; + lang: Array; + os: { + distribution: string; + name: string; + type: string; + version: string; + }; + project: string; + proxy: { + country: string; + identity: string; + network: string; + pool: string; + }; + redirects: Array; + retry: number; + schedule?: string; + session?: string; + spider?: any; + throttler?: any; + uri: { + base_url: string; + fragment?: string; + host: string; + params?: Rec; + port: number; + query?: string; + root_domain: string; + scheme: string; + }; + url: string; + webhook?: string; }; export type ResultData = { - browser_data: { - javascript_evaluation_result?: string; - js_scenario?: { - duration: number; - executed: number; - response: any; - steps: Array<{ - action: string; - config: Rec; - duration: number; - executed: boolean; - result?: string; - success: boolean; - }>; - }; - local_storage_data?: Rec; - session_storage_data?: Rec; - websockets?: Array; - xhr_call?: Array<{ - body?: string; - headers: Rec; - method: string; - type: string; - url: string; - response: { - body: string; - duration: number; - format: string; - headers: Rec; - status: number; - }; - }>; - }; - content: string; - content_encoding: string; - content_type: string; - cookies: Array<{ - name: string; - value: string; - expires: string; - path: string; - comment: string; - domain: string; - max_age: number; - secure: boolean; - http_only: boolean; - version: string; - size: number; - }>; - data?: Rec; - dns?: Rec>>; - duration: number; - error?: { - code: string; - http_code: number; - links: Rec; - message: string; - retryable: boolean; - doc_url?: string; + browser_data: { + javascript_evaluation_result?: string; + js_scenario?: { + duration: number; + executed: number; + response: any; + steps: Array<{ + action: string; + config: Rec; + duration: number; + executed: boolean; + result?: string; + success: boolean; + }>; }; - format: string; - iframes: Array<{ - url: string; - uri: { - root_domain: string; - base_url: string; - host: string; - scheme: string; - query?: string; - fragment?: string; - port: number; - params?: Rec; - }; - content: string; - }>; - log_url: string; - reason: string; - request_headers: Rec; - response_headers: Rec; - screenshots: Rec<{ - css_selector?: string; - extension: string; + local_storage_data?: Rec; + session_storage_data?: Rec; + websockets?: Array; + xhr_call?: Array<{ + body?: string; + headers: Rec; + method: string; + type: string; + url: string; + response: { + body: string; + duration: number; format: string; - size: number; - url: string; + headers: Rec; + status: number; + }; }>; + }; + content: string; + content_encoding: string; + content_type: string; + cookies: Array<{ + name: string; + value: string; + expires: string; + path: string; + comment: string; + domain: string; + max_age: number; + secure: boolean; + http_only: boolean; + version: string; size: number; - ssl?: { - certs: Array>; + }>; + data?: Rec; + dns?: Rec>>; + duration: number; + error?: { + code: string; + http_code: number; + links: Rec; + message: string; + retryable: boolean; + doc_url?: string; + }; + format: string; + iframes: Array<{ + url: string; + uri: { + root_domain: string; + base_url: string; + host: string; + scheme: string; + query?: string; + fragment?: string; + port: number; + params?: Rec; }; - status: string; - status_code: number; - success: boolean; + content: string; + }>; + log_url: string; + reason: string; + request_headers: Rec; + response_headers: Rec; + screenshots: Rec<{ + css_selector?: string; + extension: string; + format: string; + size: number; url: string; + }>; + size: number; + ssl?: { + certs: Array>; + }; + status: string; + status_code: number; + success: boolean; + url: string; }; export class ScrapeResult { - config: ConfigData; - context: ContextData; - result: ResultData; - uuid: string; - private _selector: cheerio.CheerioAPI; + config: ConfigData; + context: ContextData; + result: ResultData; + uuid: string; + private _selector: cheerio.CheerioAPI | undefined; - constructor(data: { config: ConfigData; context: ContextData; result: ResultData; uuid: string }) { - this.config = data.config; - this.context = data.context; - this.result = data.result; - this.uuid = data.uuid; - } + constructor(data: { config: ConfigData; context: ContextData; result: ResultData; uuid: string }) { + this.config = data.config; + this.context = data.context; + this.result = data.result; + this.uuid = data.uuid; + this._selector = undefined; + } - get selector() { - if (!this._selector) { - if (!this.result.response_headers['content-type'].includes('text/html')) { - throw new errors.ContentTypeError( - `Cannot use selector on non-html content-type, received: ${this.result.response_headers['content-type']}`, - ); - } - this._selector = cheerio.load(this.result.content); - } - return this._selector; + get selector(): cheerio.CheerioAPI { + if (!this._selector) { + if (!this.result.response_headers['content-type'].includes('text/html')) { + throw new errors.ContentTypeError( + `Cannot use selector on non-html content-type, received: ${this.result.response_headers['content-type']}`, + ); + } + this._selector = cheerio.load(this.result.content); } + return this._selector; + } } -export class AccountData { - acount: { - account_id: string; - currency: string; - timezone: string; +export type ScrapeResultData = Record; + +export type AccountData = { + account: { + account_id: string; + currency: string; + timezone: string; + }; + project: { + allow_extra_usage: boolean; + allowed_networks: Array; + budget_limit: any; + budget_spent: any; + concurrency_limit?: number; + name: string; + quota_reached: boolean; + scrape_request_count: number; + scrape_request_limit: number; + tags: Array; + }; + subscription: { + billing: { + current_extra_scrape_request_price: { currency: string; amount: number }; + extra_scrape_request_price_per_10k: { currency: string; amount: number }; + ongoing_payment: { currency: string; amount: number }; + plan_price: { currency: string; amount: number }; }; - project: { - allow_extra_usage: boolean; - allowed_networks: Array; - budget_limit: any; - budget_spent: any; - concurrency_limit?: number; - name: string; - quota_reached: boolean; - scrape_request_count: number; - scrape_request_limit: number; - tags: Array; + extra_scrape_allowed: boolean; + max_concurrency: number; + period: { + start: string; + end: string; }; - subscription: { - billing: { - current_extra_scrape_request_price: { currency: string; amount: number }; - extra_scrape_request_price_per_10k: { currency: string; amount: number }; - ongoing_payment: { currency: string; amount: number }; - plan_price: { currency: string; amount: number }; - }; - extra_scrape_allowed: boolean; - max_concurrency: number; - period: { - start: string; - end: string; - }; - plan_name: string; - usage: { - schedule: { current: number; limit: number }; - spider: { current: number; limit: number }; - scrape: { - concurrent_limit: number; - concurrent_remaining: number; - concurrent_usage: number; - current: number; - extra: number; - limit: number; - remaining: number; - }; - }; + plan_name: string; + usage: { + schedule: { current: number; limit: number }; + spider: { current: number; limit: number }; + scrape: { + concurrent_limit: number; + concurrent_remaining: number; + concurrent_usage: number; + current: number; + extra: number; + limit: number; + remaining: number; + }; }; -} + }; +}; export type ScreenshotMetadata = { - extension_name: string; - upstream_status_code: number; - upstream_url: string; + extension_name: string; + upstream_status_code: number; + upstream_url: string; }; export class ScreenshotResult { - image: ArrayBuffer; - metadata: ScreenshotMetadata; - result: object; + image: ArrayBuffer; + metadata: ScreenshotMetadata; + result: object | null; - constructor(response: Response, data: ArrayBuffer) { - this.image = data; - this.metadata = this.defineMetadata(response); - this.result = this.returnRaw(response, data); // raw result - } + constructor(response: Response, data: ArrayBuffer) { + this.image = data; + this.metadata = this.defineMetadata(response); + this.result = this.decodeResponse(response, data); // raw result + } - private defineMetadata(response: Response): ScreenshotMetadata { - const contentType = response.headers.get('content-type'); - let extension_name; - if (contentType) { - extension_name = contentType.split('/')[1].split(';')[0]; - } - return { - extension_name: extension_name, - upstream_status_code: parseInt(response.headers.get('X-Scrapfly-Upstream-Http-Code'), 10), - upstream_url: response.headers.get('X-Scrapfly-Upstream-Url'), - }; + private defineMetadata(response: Response): ScreenshotMetadata { + const contentType = response.headers.get('content-type'); + let extension_name = ''; + if (contentType) { + extension_name = contentType.split('/')[1].split(';')[0]; } + return { + extension_name: extension_name, + upstream_status_code: parseInt(response.headers.get('X-Scrapfly-Upstream-Http-Code') || '200', 10), + upstream_url: response.headers.get('X-Scrapfly-Upstream-Url') || '', + }; + } - private returnRaw(response: Response, data: ArrayBuffer): Promise { - if (response.headers.get('content-encoding') == 'gzip') { - return null - } - return JSON.parse(new TextDecoder().decode(data)); + private decodeResponse(response: Response, data: ArrayBuffer): object | null { + if (response.headers.get('content-type') === 'json') { + return JSON.parse(new TextDecoder().decode(data)); } + return null; + } } export class ExtractionResult { - data: string; - content_type: string; - result: object; + data: string; + content_type: string; + result: object; - constructor(response: { data: string; content_type: string }) { - this.data = response.data; - this.content_type = response.content_type; - this.result = response; // raw data - } + constructor(response: { data: string; content_type: string }) { + this.data = response.data; + this.content_type = response.content_type; + this.result = response; // raw data + } } diff --git a/src/scrapeconfig.ts b/src/scrapeconfig.ts index d4dfdb6..f75353d 100644 --- a/src/scrapeconfig.ts +++ b/src/scrapeconfig.ts @@ -1,10 +1,10 @@ -import { urlsafe_b64encode } from './utils.js'; -import { log } from './logger.js'; -import { Rec, HttpMethod } from './types.js'; -import { ScrapeConfigError } from './errors.js'; +import { urlsafe_b64encode } from './utils.ts'; +import { log } from './logger.ts'; +import type { HttpMethod, Rec } from './types.ts'; +import { ScrapeConfigError } from './errors.ts'; export enum ScreenshotFlags { - /** + /** Options to customize the screenshot behavior Attributes: LOAD_IMAGES: Enable image rendering with the request, add extra usage for the bandwidth consumed. @@ -12,15 +12,15 @@ export enum ScreenshotFlags { BLOCK_BANNERS: Block cookies banners and overlay that cover the screen. PRINT_MEDIA_FORMAT: Render the page in the print mode. */ - LOAD_IMAGES = 'load_images', - DARK_MODE = 'dark_mode', - BLOCK_BANNERS = 'block_banners', - PRINT_MEDIA_FORMAT = 'print_media_format', - HIGH_QUALITY = 'high_quality', + LOAD_IMAGES = 'load_images', + DARK_MODE = 'dark_mode', + BLOCK_BANNERS = 'block_banners', + PRINT_MEDIA_FORMAT = 'print_media_format', + HIGH_QUALITY = 'high_quality', } export enum Format { - /** + /** Format of the scraped content. Attributes: JSON: JSON format. @@ -28,306 +28,332 @@ export enum Format { MARKDOWN: Markdown format. CLEAN_HTML: Clean HTML format. */ - JSON = 'json', - TEXT = 'text', - MARKDOWN = 'markdown', - CLEAN_HTML = 'clean_html', + JSON = 'json', + TEXT = 'text', + MARKDOWN = 'markdown', + CLEAN_HTML = 'clean_html', + RAW = 'raw', } +export enum FormatOption { + NO_LINKS = 'no_links', + NO_IMAGES = 'no_images', +} + +type ScrapeConfigOptions = { + url: string; + retry?: boolean; + method?: HttpMethod; + country?: string; + render_js?: boolean; + cache?: boolean; + cache_clear?: boolean; + cost_budget?: number; + ssl?: boolean; + dns?: boolean; + asp?: boolean; + debug?: boolean; + raise_on_upstream_error?: boolean; + cache_ttl?: number; + proxy_pool?: string; + session?: string; + tags?: string[]; + format?: Format; + format_options?: FormatOption[]; + correlation_id?: string; + cookies?: Rec; + body?: string; + data?: Rec; + headers?: Rec; + js?: string; + rendering_wait?: number; + wait_for_selector?: string; + screenshots?: Rec; + screenshot_flags?: ScreenshotFlags[]; + session_sticky_proxy?: boolean; + webhook?: string; + timeout?: number; + js_scenario?: Rec; + os?: string; + lang?: string[]; + auto_scroll?: boolean; +}; + export class ScrapeConfig { - static PUBLIC_DATACENTER_POOL = 'public_datacenter_pool'; - static PUBLIC_RESIDENTIAL_POOL = 'public_residential_pool'; + static PUBLIC_DATACENTER_POOL = 'public_datacenter_pool'; + static PUBLIC_RESIDENTIAL_POOL = 'public_residential_pool'; - url: string; - retry = true; - method: HttpMethod = 'GET'; - country?: string = null; - render_js = false; - cache = false; - cache_clear = false; - cost_budget?: number = null; - ssl = false; - dns = false; - asp = false; - debug = false; - raise_on_upstream_error = true; - cache_ttl?: number = null; - proxy_pool?: string = null; - session?: string = null; - tags: Set = new Set(); - format?: Format = null; // raw(unchanged) - correlation_id?: string = null; - cookies?: Rec = null; - body?: string = null; - data?: Rec = null; - headers?: Rec = null; - js?: string = null; - rendering_wait?: number = null; - wait_for_selector?: string = null; - session_sticky_proxy = false; - screenshots?: Rec = null; - screenshot_flags?: ScreenshotFlags[] = null; - webhook?: string = null; - timeout?: number = null; // in milliseconds - js_scenario?: Rec = null; - lang?: string[] = null; - os?: string = null; - auto_scroll?: boolean = null; + url: string; + retry = true; + method: HttpMethod = 'GET'; + country?: string | null = null; + render_js = false; + cache = false; + cache_clear = false; + cost_budget?: number; + ssl = false; + dns = false; + asp = false; + debug = false; + raise_on_upstream_error = true; + cache_ttl?: number; + proxy_pool?: string; + session?: string; + tags: Set = new Set(); + format?: Format; // raw(unchanged) + format_options?: FormatOption[]; + correlation_id?: string; + cookies?: Rec; + body?: string; + data?: Rec; + headers?: Rec; + js?: string; + rendering_wait?: number; + wait_for_selector?: string; + session_sticky_proxy = false; + screenshots?: Rec; + screenshot_flags?: ScreenshotFlags[]; + webhook?: string; + timeout?: number; // in milliseconds + js_scenario?: Rec; + lang?: string[]; + os?: string; + auto_scroll?: boolean; - constructor(options: { - url: string; - retry?: boolean; - method?: HttpMethod; - country?: string; - render_js?: boolean; - cache?: boolean; - cache_clear?: boolean; - cost_budget?: number; - ssl?: boolean; - dns?: boolean; - asp?: boolean; - debug?: boolean; - raise_on_upstream_error?: boolean; - cache_ttl?: number; - proxy_pool?: string; - session?: string; - tags?: Array; - format?: Format; - correlation_id?: string; - cookies?: Rec; - body?: string; - data?: Rec; - headers?: Rec; - js?: string; - rendering_wait?: number; - wait_for_selector?: string; - screenshots?: Rec; - screenshot_flags?: ScreenshotFlags[]; - session_sticky_proxy?: boolean; - webhook?: string; - timeout?: number; // in milliseconds - js_scenario?: Rec; - os?: string; - lang?: string[]; - auto_scroll?: boolean; - }) { - if (options.format && !Object.values(Format).includes(options.format)) { - throw new ScrapeConfigError(`Invalid format param value: ${options.format}`); - } - this.format = options.format ?? this.format; - if (options.screenshot_flags) { - options.screenshot_flags.forEach((flag) => { - if (!Object.values(ScreenshotFlags).includes(flag)) { - throw new ScrapeConfigError(`Invalid screenshot_flags param value: ${flag}`); - } - }); - } - this.url = options.url; - this.retry = options.retry ?? this.retry; - this.method = options.method ?? this.method; - this.country = options.country ?? this.country; - this.session_sticky_proxy = options.session_sticky_proxy ?? this.session_sticky_proxy; - this.render_js = options.render_js ?? this.render_js; - this.cache = options.cache ?? this.cache; - this.cache_clear = options.cache_clear ?? this.cache_clear; - this.cost_budget = options.cost_budget ?? this.cost_budget; - this.asp = options.asp ?? this.asp; - this.headers = options.headers - ? Object.fromEntries(Object.entries(options.headers).map(([k, v]) => [k.toLowerCase(), v])) - : {}; - this.raise_on_upstream_error = options.raise_on_upstream_error ?? this.raise_on_upstream_error; - this.cache_ttl = options.cache_ttl ?? this.cache_ttl; - this.proxy_pool = options.proxy_pool ?? this.proxy_pool; - this.session = options.session ?? this.session; - this.tags = new Set(options.tags) ?? this.tags; - this.format = options.format ?? this.format; - this.correlation_id = options.correlation_id ?? this.correlation_id; - this.cookies = options.cookies - ? Object.fromEntries(Object.entries(options.cookies).map(([k, v]) => [k.toLowerCase(), v])) - : {}; - this.body = options.body ?? this.body; - this.data = options.data ?? this.data; - this.js = options.js ?? this.js; - this.rendering_wait = options.rendering_wait ?? this.rendering_wait; - this.wait_for_selector = options.wait_for_selector ?? this.wait_for_selector; - this.screenshots = options.screenshots ?? this.screenshots; - this.screenshot_flags = options.screenshot_flags ?? this.screenshot_flags; - this.webhook = options.webhook ?? this.webhook; - this.timeout = options.timeout ?? this.timeout; - this.js_scenario = options.js_scenario ?? this.js_scenario; - this.os = options.os ?? this.os; - this.lang = options.lang ?? this.lang; - this.auto_scroll = options.auto_scroll ?? this.auto_scroll; - this.dns = options.dns ?? this.dns; - this.ssl = options.ssl ?? this.ssl; - this.debug = options.debug ?? this.debug; - if (this.body && this.data) { - throw new ScrapeConfigError('Cannot set both body and data'); + constructor(options: ScrapeConfigOptions) { + this.validateOptions(options); + if (options.format && !Object.values(Format).includes(options.format)) { + throw new ScrapeConfigError(`Invalid format param value: ${options.format}`); + } + this.format = options.format ?? this.format; + if (options.screenshot_flags) { + options.screenshot_flags.forEach((flag) => { + if (!Object.values(ScreenshotFlags).includes(flag)) { + throw new ScrapeConfigError(`Invalid screenshot_flags param value: ${flag}`); } - if (['POST', 'PUT', 'PATCH'].includes(this.method)) { - if (this.data && !this.body) { - if (!this.headers['content-type']) { - this.headers['content-type'] = 'application/x-www-form-urlencoded'; - this.body = new URLSearchParams(this.data).toString(); - } else { - if (this.headers['content-type']?.includes('application/json')) { - this.body = JSON.stringify(this.data); - } else if (this.headers['content-type']?.includes('application/x-www-form-urlencoded')) { - this.body = new URLSearchParams(this.data).toString(); - } else { - throw new ScrapeConfigError( - `Content-Type "${this.headers['content-type']}" not supported, use body parameter to pass pre encoded body according to your content type`, - ); - } - } - } else if (this.body && !this.data) { - this.headers['content-type'] = 'text/plain'; - } + }); + } + this.url = options.url; + this.retry = options.retry ?? this.retry; + this.method = options.method ?? this.method; + this.country = options.country ?? this.country; + this.session_sticky_proxy = options.session_sticky_proxy ?? this.session_sticky_proxy; + this.render_js = options.render_js ?? this.render_js; + this.cache = options.cache ?? this.cache; + this.cache_clear = options.cache_clear ?? this.cache_clear; + this.cost_budget = options.cost_budget ?? this.cost_budget; + this.asp = options.asp ?? this.asp; + this.headers = options.headers + ? Object.fromEntries(Object.entries(options.headers).map(([k, v]) => [k.toLowerCase(), v])) + : {}; + this.raise_on_upstream_error = options.raise_on_upstream_error ?? this.raise_on_upstream_error; + this.cache_ttl = options.cache_ttl ?? this.cache_ttl; + this.proxy_pool = options.proxy_pool ?? this.proxy_pool; + this.session = options.session ?? this.session; + this.tags = new Set(options.tags) ?? this.tags; + this.format = options.format ?? this.format; + this.format_options = options.format_options ?? this.format_options; + this.correlation_id = options.correlation_id ?? this.correlation_id; + this.cookies = options.cookies + ? Object.fromEntries(Object.entries(options.cookies).map(([k, v]) => [k.toLowerCase(), v])) + : {}; + this.body = options.body ?? this.body; + this.data = options.data ?? this.data; + this.js = options.js ?? this.js; + this.rendering_wait = options.rendering_wait ?? this.rendering_wait; + this.wait_for_selector = options.wait_for_selector ?? this.wait_for_selector; + this.screenshots = options.screenshots ?? this.screenshots; + this.screenshot_flags = options.screenshot_flags ?? this.screenshot_flags; + this.webhook = options.webhook ?? this.webhook; + this.timeout = options.timeout ?? this.timeout; + this.js_scenario = options.js_scenario ?? this.js_scenario; + this.os = options.os ?? this.os; + this.lang = options.lang ?? this.lang; + this.auto_scroll = options.auto_scroll ?? this.auto_scroll; + this.dns = options.dns ?? this.dns; + this.ssl = options.ssl ?? this.ssl; + this.debug = options.debug ?? this.debug; + if (this.body && this.data) { + throw new ScrapeConfigError('Cannot set both body and data'); + } + if (['POST', 'PUT', 'PATCH'].includes(this.method)) { + if (this.data && !this.body) { + if (!this.headers['content-type']) { + this.headers['content-type'] = 'application/x-www-form-urlencoded'; + this.body = new URLSearchParams(this.data).toString(); + } else { + if (this.headers['content-type']?.includes('application/json')) { + this.body = JSON.stringify(this.data); + } else if (this.headers['content-type']?.includes('application/x-www-form-urlencoded')) { + this.body = new URLSearchParams(this.data).toString(); + } else { + throw new ScrapeConfigError( + `Content-Type "${ + this.headers['content-type'] + }" not supported, use body parameter to pass pre encoded body according to your content type`, + ); + } } + } else if (this.body && !this.data) { + this.headers['content-type'] = 'text/plain'; + } } + } - toApiParams(options: { key: string }): Record { - const params: Record = { - key: options.key, - }; - params.url = this.url; - if (this.country) { - params.country = this.country; - } - if (Object.keys(this.headers).length > 0) { - Object.entries(this.headers).forEach(([key, value]) => { - params[`headers[${key}]`] = value; - }); - } + private validateOptions(options: Partial) { + const validKeys = new Set(Object.keys(this) as Array); + for (const key in options) { + if (!validKeys.has(key as keyof ScrapeConfig)) { + throw new ScrapeConfigError(`Invalid option provided: ${key}`); + } + } + } - if (Object.keys(this.cookies).length > 0) { - const cookiesAsHeader = [...Object.entries(this.cookies)] - .map(([key, value]) => `${key}=${value}`) - .join('; '); + toApiParams(options: { key: string }): Record { + const params: Record = { + key: options.key, + }; + params.url = this.url; + if (this.country) { + params.country = this.country; + } + if (this.headers !== undefined && Object.keys(this.headers).length > 0) { + Object.entries(this.headers).forEach(([key, value]) => { + params[`headers[${key}]`] = value; + }); + } - if (params['headers[cookie]']) { - // if current cookie value doesn't have a ';' at the end, add it. - if (params['headers[cookie]'][params['headers[cookie]'].length - 1] !== ';') { - params['headers[cookie]'] += ';'; - } - params['headers[cookie]'] += ` ${cookiesAsHeader}`; - } else { - params['headers[cookie]'] = cookiesAsHeader; - } - } - if (this.webhook) { - params.webhook_name = this.webhook; - } - if (this.timeout) { - params.timeout = this.timeout; - } - if (this.render_js === true) { - params.render_js = true; - if (this.wait_for_selector !== null) { - params.wait_for_selector = this.wait_for_selector; - } - if (this.js !== null) { - params.js = urlsafe_b64encode(this.js); - } - if (this.js_scenario !== null) { - params.js_scenario = urlsafe_b64encode(JSON.stringify(this.js_scenario)); - } - if (this.rendering_wait !== null) { - params.rendering_wait = this.rendering_wait; - } - if (this.screenshots) { - Object.keys(this.screenshots).forEach((key) => { - params[`screenshots[${key}]`] = this.screenshots[key]; - }); - if (this.screenshot_flags) { - params.screenshot_flags = this.screenshot_flags.join(','); - } - } else { - if (this.screenshot_flags) { - log.warn('Params "screenshot_flags" is ignored. Works only if screenshots is enabled'); - } - } - if (this.auto_scroll !== null) { - params.auto_scroll = this.auto_scroll; - } - } else { - if (this.wait_for_selector !== null) { - log.warn('Params "wait_for_selector" is ignored. Works only if render_js is enabled'); - } - if (this.screenshots !== null) { - log.warn('Params "screenshots" is ignored. Works only if render_js is enabled'); - } - if (this.js_scenario !== null) { - log.warn('Params "js_scenario" is ignored. Works only if render_js is enabled'); - } - if (this.js !== null) { - log.warn('Params "js" is ignored. Works only if render_js is enabled'); - } - if (this.rendering_wait !== null) { - log.warn('Params "rendering_wait" is ignored. Works only if render_js is enabled'); - } - } + if (this.cookies !== undefined && Object.keys(this.cookies).length > 0) { + const cookiesAsHeader = [...Object.entries(this.cookies)] + .map(([key, value]) => `${key}=${value}`) + .join('; '); - if (this.asp === true) { - params.asp = true; - } - if (this.retry === false) { - params.retry = false; - } - if (this.cache === true) { - params.cache = true; - if (this.cache_clear) { - params.cache_clear = true; - } - if (this.cache_ttl) { - params.cache_ttl = this.cache_ttl; - } - } else { - if (this.cache_clear) { - log.warn('Params "cache_clear" is ignored. Works only if cache is enabled'); - } - if (this.cache_ttl) { - log.warn('Params "cache_ttl" is ignored. Works only if cache is enabled'); - } - } - if (this.dns === true) { - params.dns = true; - } - if (this.ssl === true) { - params.ssl = true; + if (params['headers[cookie]']) { + // if current cookie value doesn't have a ';' at the end, add it. + if (params['headers[cookie]'][params['headers[cookie]'].length - 1] !== ';') { + params['headers[cookie]'] += ';'; } - if (this.tags.size > 0) { - params.tags = Array.from(this.tags).join(','); - } - if (this.format) { - params.format = this.format.valueOf(); - } - if (this.correlation_id) { - params.correlation_id = this.correlation_id; - } - if (this.session) { - params.session = this.session; - if (this.session_sticky_proxy) { - params.session_sticky_proxy = true; - } - } else { - if (this.session_sticky_proxy) { - log.warn('Params "session_sticky_proxy" is ignored. Works only if session is enabled'); - } - } - if (this.debug === true) { - params.debug = true; - } - if (this.proxy_pool) { - params.proxy_pool = this.proxy_pool; - } - if (this.lang !== null) { - params.lang = this.lang.join(','); + params['headers[cookie]'] += ` ${cookiesAsHeader}`; + } else { + params['headers[cookie]'] = cookiesAsHeader; + } + } + if (this.webhook) { + params.webhook_name = this.webhook; + } + if (this.timeout) { + params.timeout = this.timeout; + } + if (this.render_js === true) { + params.render_js = true; + if (this.wait_for_selector !== undefined) { + params.wait_for_selector = this.wait_for_selector; + } + if (this.js !== undefined) { + params.js = urlsafe_b64encode(this.js); + } + if (this.js_scenario !== undefined) { + params.js_scenario = urlsafe_b64encode(JSON.stringify(this.js_scenario)); + } + if (this.rendering_wait !== undefined) { + params.rendering_wait = this.rendering_wait; + } + if (this.screenshots !== undefined) { + Object.keys(this.screenshots).forEach((key) => { + params[`screenshots[${key}]`] = (this.screenshots || {})[key]; + }); + if (this.screenshot_flags) { + params.screenshot_flags = this.screenshot_flags.join(','); } - if (this.os !== null) { - params.os = this.os; + } else { + if (this.screenshot_flags) { + log.warn('Params "screenshot_flags" is ignored. Works only if screenshots is enabled'); } + } + if (this.auto_scroll !== undefined) { + params.auto_scroll = this.auto_scroll; + } + } else { + if (this.wait_for_selector !== undefined) { + log.warn('Params "wait_for_selector" is ignored. Works only if render_js is enabled'); + } + if (this.screenshots !== undefined) { + log.warn('Params "screenshots" is ignored. Works only if render_js is enabled'); + } + if (this.js_scenario !== undefined) { + log.warn('Params "js_scenario" is ignored. Works only if render_js is enabled'); + } + if (this.js !== undefined) { + log.warn('Params "js" is ignored. Works only if render_js is enabled'); + } + if (this.rendering_wait !== undefined) { + log.warn('Params "rendering_wait" is ignored. Works only if render_js is enabled'); + } + } - return params; + if (this.asp === true) { + params.asp = true; + } + if (this.retry === false) { + params.retry = false; } + if (this.cache === true) { + params.cache = true; + if (this.cache_clear) { + params.cache_clear = true; + } + if (this.cache_ttl) { + params.cache_ttl = this.cache_ttl; + } + } else { + if (this.cache_clear) { + log.warn('Params "cache_clear" is ignored. Works only if cache is enabled'); + } + if (this.cache_ttl) { + log.warn('Params "cache_ttl" is ignored. Works only if cache is enabled'); + } + } + if (this.dns === true) { + params.dns = true; + } + if (this.ssl === true) { + params.ssl = true; + } + if (this.tags.size > 0) { + params.tags = Array.from(this.tags).join(','); + } + if (this.format) { + params.format = this.format.valueOf(); + if (this.format_options) { + params.format += ':' + this.format_options.join(','); + } + } + if (this.correlation_id) { + params.correlation_id = this.correlation_id; + } + if (this.session) { + params.session = this.session; + if (this.session_sticky_proxy) { + params.session_sticky_proxy = true; + } + } else { + if (this.session_sticky_proxy) { + log.warn('Params "session_sticky_proxy" is ignored. Works only if session is enabled'); + } + } + if (this.debug === true) { + params.debug = true; + } + if (this.proxy_pool) { + params.proxy_pool = this.proxy_pool; + } + if (this.lang !== undefined) { + params.lang = this.lang.join(','); + } + if (this.os !== undefined) { + params.os = this.os; + } + + return params; + } } diff --git a/src/screenshotconfig.ts b/src/screenshotconfig.ts index 73c57cb..df9d90d 100644 --- a/src/screenshotconfig.ts +++ b/src/screenshotconfig.ts @@ -1,9 +1,9 @@ -import { ScreenshotConfigError } from './errors.js'; -import { urlsafe_b64encode } from './utils.js'; -import { log } from './logger.js'; +import { ScreenshotConfigError } from './errors.ts'; +import { urlsafe_b64encode } from './utils.ts'; +import { log } from './logger.ts'; export enum Options { - /** + /** Options to customize the screenshot behavior Attributes: LOAD_IMAGES: Enable image rendering with the request, add extra usage for the bandwidth consumed. @@ -11,14 +11,14 @@ export enum Options { BLOCK_BANNERS: Block cookies banners and overlay that cover the screen. PRINT_MEDIA_FORMAT: Render the page in the print mode. */ - LOAD_IMAGES = 'load_images', - DARK_MODE = 'dark_mode', - BLOCK_BANNERS = 'block_banners', - PRINT_MEDIA_FORMAT = 'print_media_format', + LOAD_IMAGES = 'load_images', + DARK_MODE = 'dark_mode', + BLOCK_BANNERS = 'block_banners', + PRINT_MEDIA_FORMAT = 'print_media_format', } export enum Format { - /** + /** Format of the screenshot image. Attributes: JPG: JPG format. @@ -26,142 +26,154 @@ export enum Format { WEBP: WEBP format. GIF: GIF format. */ - JPG = 'jpg', - PNG = 'png', - WEBP = 'webp', - GIF = 'gif', + JPG = 'jpg', + PNG = 'png', + WEBP = 'webp', + GIF = 'gif', } +type ScreenshotConfigOptions = { + url: string; + format?: Format; + capture?: string; + resolution?: string; + country?: string; + timeout?: number; + rendering_wait?: number; + wait_for_selector?: string; + options?: Options[]; + auto_scroll?: boolean; + js?: string; + cache?: boolean; + cache_ttl?: number; + cache_clear?: boolean; + webhook?: string; +}; + export class ScreenshotConfig { - url: string; - format?: Format = null; - capture?: string = null; - resolution?: string = null; - country?: string = null; - timeout?: number = null; // in milliseconds - rendering_wait?: number = null; // in milliseconds - wait_for_selector?: string = null; - options?: Options[] = null; - auto_scroll?: boolean = null; - js?: string = null; - cache?: boolean = null; - cache_ttl?: boolean = null; - cache_clear?: boolean = null; - webhook?: string = null; - - constructor(options: { - url: string; - format?: Format; - capture?: string; - resolution?: string; - country?: string; - timeout?: number; - rendering_wait?: number; - wait_for_selector?: string; - options?: Options[]; - auto_scroll?: boolean; - js?: string; - cache?: boolean; - cache_ttl?: boolean; - cache_clear?: boolean; - webhook?: string; - }) { - if (options.format && !Object.values(Format).includes(options.format)) { - throw new ScreenshotConfigError(`Invalid format param value: ${options.format}`); - } - this.format = options.format ?? this.format; - // Validate options against the enum - if (options.options) { - options.options.forEach((opt) => { - if (!Object.values(Options).includes(opt)) { - throw new ScreenshotConfigError(`Invalid options param value: ${opt}`); - } - }); + url: string; + format?: Format; + capture?: string; + resolution?: string; + country?: string = undefined; + timeout?: number; + rendering_wait?: number; + wait_for_selector?: string; + options?: Options[]; + auto_scroll?: boolean; + js?: string; + cache?: boolean; + cache_ttl?: number; + cache_clear?: boolean; + webhook?: string; + + constructor(options: ScreenshotConfigOptions) { + this.validateOptions(options); + if (options.format && !Object.values(Format).includes(options.format)) { + throw new ScreenshotConfigError(`Invalid format param value: ${options.format}`); + } + this.format = options.format ?? this.format; + // Validate options against the enum + if (options.options) { + options.options.forEach((opt) => { + if (!Object.values(Options).includes(opt)) { + throw new ScreenshotConfigError(`Invalid options param value: ${opt}`); } - this.url = options.url; - this.format = options.format ?? this.format; - this.capture = options.capture ?? this.capture; - this.resolution = options.resolution ?? this.resolution; - this.country = options.country ?? this.country; - this.timeout = options.timeout ?? this.timeout; - this.rendering_wait = options.rendering_wait ?? this.rendering_wait; - this.wait_for_selector = options.wait_for_selector ?? this.wait_for_selector; - this.options = options.options ?? this.options; - this.auto_scroll = options.auto_scroll ?? this.auto_scroll; - this.js = options.js ?? this.js; - this.cache = options.cache ?? this.cache; - this.cache_ttl = options.cache_ttl ?? this.cache_ttl; - this.cache_clear = options.cache_clear ?? this.cache_clear; - this.webhook = options.webhook; + }); + } + this.url = options.url; + this.format = options.format ?? this.format; + this.capture = options.capture ?? this.capture; + this.resolution = options.resolution ?? this.resolution; + this.country = options.country ?? this.country; + this.timeout = options.timeout ?? this.timeout; + this.rendering_wait = options.rendering_wait ?? this.rendering_wait; + this.wait_for_selector = options.wait_for_selector ?? this.wait_for_selector; + this.options = options.options ?? this.options; + this.auto_scroll = options.auto_scroll ?? this.auto_scroll; + this.js = options.js ?? this.js; + this.cache = options.cache ?? this.cache; + this.cache_ttl = options.cache_ttl ?? this.cache_ttl; + this.cache_clear = options.cache_clear ?? this.cache_clear; + this.webhook = options.webhook; + } + + private validateOptions(options: Partial) { + const validKeys = new Set(Object.keys(this) as Array); + for (const key in options) { + if (!validKeys.has(key as keyof ScreenshotConfig)) { + throw new ScreenshotConfigError(`Invalid option provided: ${key}`); + } } + } - toApiParams(options: { key: string }): Record { - const params: Record = { - key: options.key, - }; - params.url = this.url; + toApiParams(options: { key: string }): Record { + const params: Record = { + key: options.key, + }; + params.url = this.url; - if (this.format) { - params.format = this.format.valueOf(); - } + if (this.format) { + params.format = this.format.valueOf(); + } - if (this.capture) { - params.capture = this.capture; - } + if (this.capture) { + params.capture = this.capture; + } - if (this.resolution) { - params.resolution = this.resolution; - } + if (this.resolution) { + params.resolution = this.resolution; + } - if (this.country) { - params.country = this.country; - } + if (this.country) { + params.country = this.country; + } - if (this.timeout) { - params.timeout = this.timeout; - } + if (this.timeout) { + params.timeout = this.timeout; + } - if (this.rendering_wait) { - params.rendering_wait = this.rendering_wait; - } + if (this.rendering_wait) { + params.rendering_wait = this.rendering_wait; + } - if (this.wait_for_selector) { - params.wait_for_selector = this.wait_for_selector; - } + if (this.wait_for_selector) { + params.wait_for_selector = this.wait_for_selector; + } - if (this.options) { - params.options = this.options.join(','); - } + if (this.options) { + params.options = this.options.join(','); + } - if (this.auto_scroll === true) { - params.auto_scroll = this.auto_scroll; - } + if (this.auto_scroll === true) { + params.auto_scroll = this.auto_scroll; + } - if (this.js) { - params.js = urlsafe_b64encode(this.js); - } + if (this.js) { + params.js = urlsafe_b64encode(this.js); + } - if (this.cache === true) { - params.cache = this.cache; - if (this.cache_ttl) { - params.cache_ttl = this.cache_ttl; - } - if (this.cache_clear === true) { - params.cache_clear = this.cache_clear; - } - } else { - if (this.cache_ttl) { - log.warn('Params "cache_ttl" is ignored. Works only if cache is enabled'); - } - if (this.cache_clear) { - log.warn('Params "cache_clear" is ignored. Works only if cache is enabled'); - } - } + if (this.cache === true) { + params.cache = this.cache; + if (this.cache_ttl) { + params.cache_ttl = this.cache_ttl; + } + if (this.cache_clear === true) { + params.cache_clear = this.cache_clear; + } + } else { + if (this.cache_ttl) { + log.warn('Params "cache_ttl" is ignored. Works only if cache is enabled'); + } + if (this.cache_clear) { + log.warn('Params "cache_clear" is ignored. Works only if cache is enabled'); + } + } - if (this.webhook) { - params.webhook_name = this.webhook; - } - - return params; + if (this.webhook) { + params.webhook_name = this.webhook; } + + return params; + } } diff --git a/src/utils.ts b/src/utils.ts index 6f51327..5e3d216 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,49 @@ export function urlsafe_b64encode(data: string): string { - return Buffer.from(data, 'utf-8').toString('base64') - .replace(/\+/g, '-') // Replace all instances of '+' with '-' - .replace(/\//g, '_') // Replace all instances of '/' with '_' - .replace(/=+$/, ''); // Remove trailing '=' characters + const encoder = new TextEncoder(); + const encoded = encoder.encode(data); + const base64 = btoa(String.fromCharCode(...encoded)) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+$/, ''); + return base64; +} + +export async function fetchRetry( + config: Request, + init: RequestInit = {}, + retries: number = 3, + retryDelay: number = 1000, + timeout: number = 160000, // Timeout in milliseconds +): Promise { + let lastError: any = null; + + for (let attempt = 1; attempt <= retries; attempt++) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + const response = await fetch(config, { ...init, signal: controller.signal }); + clearTimeout(timeoutId); + // retry 5xx status codes + if (response.status >= 500 && response.status < 600) { + lastError = new Error(`Fetch failed with status: ${response.status}`); + if (attempt < retries) { + await new Promise((resolve) => setTimeout(resolve, retryDelay)); + } + } else { + return response; + } + } catch (error) { + clearTimeout(timeoutId); + lastError = error; + + if (attempt === retries || error.name === 'AbortError') { + throw error; + } + + await new Promise((resolve) => setTimeout(resolve, retryDelay)); + } + } + + throw lastError; } diff --git a/tsconfig.json b/tsconfig.json index 7e06dfd..f7eb880 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,6 @@ "strictNullChecks": false, "esModuleInterop": true }, - "include": ["src/**/*", "__tests__/**/*"], - "exclude": ["examples/**/*"] + "include": ["src/**/*", "__tests__/**/*", "typings.d.ts"], + "exclude": ["examples/**/*", "build", "node_modules"] } From 363659a6a6609261f33645a99671846eaf224bf3 Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sat, 27 Jul 2024 00:01:38 +0700 Subject: [PATCH 2/8] mark cf issues and bump version --- deno.json | 2 +- publish-jsr.sh | 4 ++-- src/extractionconfig.ts | 1 + src/result.ts | 6 +++--- src/utils.ts | 1 + 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/deno.json b/deno.json index 6ba66fb..71f1f73 100644 --- a/deno.json +++ b/deno.json @@ -4,7 +4,7 @@ }, "name": "@scrapfly/scrapfly-sdk", "exports": "./src/main.ts", - "version": "0.6.0", + "version": "0.6.1", "description": "SDK for Scrapfly.io API for web scraping, screenshotting and data extraction", "tasks": { "start": "deno run --allow-net --allow-read src/main.ts", diff --git a/publish-jsr.sh b/publish-jsr.sh index a0695d2..0582e37 100755 --- a/publish-jsr.sh +++ b/publish-jsr.sh @@ -17,6 +17,6 @@ cp LICENSE build/ cd build # Publish the package -deno task test +# deno task test rm -r __tests__ -deno publish --dry-run --allow-dirty +deno publish --allow-dirty diff --git a/src/extractionconfig.ts b/src/extractionconfig.ts index d5d1be2..76e1ad7 100644 --- a/src/extractionconfig.ts +++ b/src/extractionconfig.ts @@ -115,6 +115,7 @@ export class ExtractionConfig { } if (this.is_document_compressed === false) { if (this.document_compression_format === CompressionFormat.GZIP) { + // XXX: This breaks cloudflare workers as they don't support node:zlib const compressed = gzipSync(Buffer.from(this.body as string, 'utf-8')); this.body = new Uint8Array(compressed); } else { diff --git a/src/result.ts b/src/result.ts index b0919d2..3b7ffb8 100644 --- a/src/result.ts +++ b/src/result.ts @@ -310,11 +310,11 @@ export class ScreenshotResult { export class ExtractionResult { data: string; content_type: string; - result: object; + data_quality?: string; - constructor(response: { data: string; content_type: string }) { + constructor(response: { data: string; content_type: string, data_quality?: string }) { this.data = response.data; this.content_type = response.content_type; - this.result = response; // raw data + this.data_quality = response.data_quality; } } diff --git a/src/utils.ts b/src/utils.ts index 5e3d216..5b08049 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -22,6 +22,7 @@ export async function fetchRetry( const timeoutId = setTimeout(() => controller.abort(), timeout); try { + // XXX: this breaks cloudflare workers as they don't support init options const response = await fetch(config, { ...init, signal: controller.signal }); clearTimeout(timeoutId); // retry 5xx status codes From 5930110da437d1b4ffafdcb5156445d1035fe123 Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sun, 28 Jul 2024 16:44:07 +0700 Subject: [PATCH 3/8] update to handle cf workers --- .github/workflows/publish.yaml | 27 ++++++++++++++++-------- README.md | 10 +++++++-- __tests__/config/extraction.test.ts | 5 +++-- __tests__/utils.test.ts | 6 ++++-- build.ts | 32 ++--------------------------- src/deps.ts | 4 ++-- src/extractionconfig.ts | 25 +++++++++++----------- src/result.ts | 2 +- src/utils.ts | 9 +------- 9 files changed, 51 insertions(+), 69 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 9e44338..173d6d0 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,3 +1,4 @@ + name: Publish to NPM on: @@ -12,17 +13,25 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: volta-cli/action@v1 - - run: npm ci --no-audit - - run: npm run lint --if-present - - run: npm test - - run: npm run build --if-present - env: - CI: true + + - name: Install Deno + run: | + curl -fsSL https://deno.land/x/install/install.sh | sh + echo "DENO_INSTALL=/home/runner/.deno" >> $GITHUB_ENV + echo "$DENO_INSTALL/bin" >> $GITHUB_PATH + + - name: Run Deno build script + run: deno run -A build.ts + + - name: Navigate to npm directory + run: cd ./npm + - name: Setup .npmrc file to publish to npm run: | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc env: - NPM_TOKEN: ${{secrets.NPM_AUTOMATION_TOKEN}} + NPM_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} + - name: Publish to NPM - run: npm publish \ No newline at end of file + run: npm publish --dry-run + working-directory: ./npm diff --git a/README.md b/README.md index ef8540d..b3f47bd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # Scrapfly SDK -`npm install scrapfly-sdk` +`npm install scrapfly-sdk` +`deno add jsr:@scrapfly/scrapfly-sdk` +`bun jsr add @scrapfly/scrapfly-sdk` -Typescript/NodeJS SDK for [Scrapfly.io](https://scrapfly.io/) web scraping API which allows to: +Typescript/Javascript SDK for [Scrapfly.io](https://scrapfly.io/) web scraping API which allows to: - Scrape the web without being blocked. - Use headless browsers to access Javascript-powered page data. @@ -11,6 +13,10 @@ Typescript/NodeJS SDK for [Scrapfly.io](https://scrapfly.io/) web scraping API w For web scraping guides see [our blog](https://scrapfly.io/blog/) and [#scrapeguide](https://scrapfly.io/blog/tag/scrapeguide/) tag for how to scrape specific targets. +The SDK is distributed through: +- [npmjs.com/package/scrapfly-sdk](https://www.npmjs.com/package/scrapfly-sdk) +- [jsr.io/@scrapfly/scrapfly-sdk](https://jsr.io/@scrapfly/scrapfly-sdk) + ## Quick Intro 1. Register a [Scrapfly account for free](https://scrapfly.io/register) diff --git a/__tests__/config/extraction.test.ts b/__tests__/config/extraction.test.ts index f18404d..b6d2be5 100644 --- a/__tests__/config/extraction.test.ts +++ b/__tests__/config/extraction.test.ts @@ -114,7 +114,8 @@ Deno.test('url param generation: sets extraction_model', async () => { }); }); -Deno.test({ +// XXX: could add auto compression but support is difficult +/* Deno.test({ name: 'url param generation: compresses body', async fn() { const config = new ExtractionConfig({ @@ -139,7 +140,7 @@ Deno.test({ sanitizeResources: false, sanitizeOps: false, }); - + */ Deno.test('url param generation: fails to missing compression state with declared compression format', async () => { const config = new ExtractionConfig({ body: input_html, diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index af7e0d6..72faa70 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -81,7 +81,7 @@ Deno.test('fetchRetry: fails after max retries', async () => { await assertRejects( async () => { - await fetchRetry(request, {}, 3, 1000); + await fetchRetry(request, 3); }, Error, 'Fetch failed with status: 500' @@ -92,6 +92,8 @@ Deno.test('fetchRetry: fails after max retries', async () => { fetchStub.restore(); }); +// XXX: should we support built-in timeout? +/* Deno.test('fetchRetry: fails due to timeout', async () => { const request = new Request('https://httpbin.dev/delay/3'); @@ -103,4 +105,4 @@ Deno.test('fetchRetry: fails due to timeout', async () => { Error, 'The signal has been aborted' ); -}); \ No newline at end of file +}); */ \ No newline at end of file diff --git a/build.ts b/build.ts index 5295f50..7fca26a 100644 --- a/build.ts +++ b/build.ts @@ -1,31 +1,4 @@ import { build, emptyDir } from "@deno/dnt"; -import { rollup } from "npm:rollup"; -import resolve from "npm:@rollup/plugin-node-resolve"; -import commonjs from "npm:@rollup/plugin-commonjs"; -import { terser } from "npm:rollup-plugin-terser"; - -async function bundleForBrowser() { - console.log("Bundling for browser..."); - const bundle = await rollup({ - input: "npm/esm/main.js", - plugins: [ - resolve({ browser: true, preferBuiltins: false }), - commonjs(), - terser(), - ], - logLevel: 'debug', - }); - - console.log("Writing to npm/dist/bundle.js"); - await bundle.write({ - file: "npm/dist/bundle.js", - format: "esm", - sourcemap: true, - }); - console.log("Closing Rollup"); - await bundle.close(); -} - await emptyDir("./npm"); const { version, description } = JSON.parse(Deno.readTextFileSync("deno.json")); @@ -53,13 +26,12 @@ await build({ url: "https://github.com/scrapfly/typescript-scrapfly/issues", }, homepage: "https://scrapfly.io/", - main: "./esm/src/main.js", // Point to the ESM output - types: "./esm/src/main.d.ts", // Point to the TypeScript declarations + main: "./esm/main.js", + types: "./esm/main.d.ts", }, postBuild: async () => { Deno.copyFileSync("LICENSE", "npm/LICENSE"); Deno.copyFileSync("README.md", "npm/README.md"); - await bundleForBrowser(); }, }); diff --git a/src/deps.ts b/src/deps.ts index cfa8b92..d495228 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,4 +1,4 @@ -import * as cheerio from "npm:cheerio@1.0.0-rc.12"; +import * as cheerio from 'https://cdn.skypack.dev/cheerio@1.0.0-rc.12?dts'; import * as path from 'jsr:@std/path@1.0.1'; -export { cheerio, path}; \ No newline at end of file +export { cheerio, path }; diff --git a/src/extractionconfig.ts b/src/extractionconfig.ts index 76e1ad7..51bac22 100644 --- a/src/extractionconfig.ts +++ b/src/extractionconfig.ts @@ -1,7 +1,5 @@ import * as errors from './errors.ts'; import { urlsafe_b64encode } from './utils.ts'; -import { gzipSync } from 'node:zlib'; -import { Buffer } from 'node:buffer'; export enum CompressionFormat { /** @@ -114,17 +112,18 @@ export class ExtractionConfig { ); } if (this.is_document_compressed === false) { - if (this.document_compression_format === CompressionFormat.GZIP) { - // XXX: This breaks cloudflare workers as they don't support node:zlib - const compressed = gzipSync(Buffer.from(this.body as string, 'utf-8')); - this.body = new Uint8Array(compressed); - } else { - throw new errors.ExtractionConfigError( - `Auto compression for ${this.document_compression_format} format isn't available. ` + - `You can manually compress to ${this.document_compression_format}` + - `or choose the gzip format for auto compression`, - ); - } + // if (this.document_compression_format === CompressionFormat.GZIP) { + // XXX: This breaks cloudflare workers for some reason + // const compressed = gzip(new TextEncoder().encode(this.body as string)); + // this.body = new Uint8Array(compressed); + // throw new Error("automatic gzip is not supported yet, pass gzipped "); + // } else { + throw new errors.ExtractionConfigError( + `Auto compression for ${this.document_compression_format} format isn't available. ` + + `You can manually compress to ${this.document_compression_format}` + + `or choose the gzip format for auto compression`, + ); + // } } } diff --git a/src/result.ts b/src/result.ts index 3b7ffb8..2500002 100644 --- a/src/result.ts +++ b/src/result.ts @@ -312,7 +312,7 @@ export class ExtractionResult { content_type: string; data_quality?: string; - constructor(response: { data: string; content_type: string, data_quality?: string }) { + constructor(response: { data: string; content_type: string; data_quality?: string }) { this.data = response.data; this.content_type = response.content_type; this.data_quality = response.data_quality; diff --git a/src/utils.ts b/src/utils.ts index 5b08049..e920bf4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,21 +10,15 @@ export function urlsafe_b64encode(data: string): string { export async function fetchRetry( config: Request, - init: RequestInit = {}, retries: number = 3, retryDelay: number = 1000, - timeout: number = 160000, // Timeout in milliseconds ): Promise { let lastError: any = null; for (let attempt = 1; attempt <= retries; attempt++) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - try { // XXX: this breaks cloudflare workers as they don't support init options - const response = await fetch(config, { ...init, signal: controller.signal }); - clearTimeout(timeoutId); + const response = await fetch(config); // retry 5xx status codes if (response.status >= 500 && response.status < 600) { lastError = new Error(`Fetch failed with status: ${response.status}`); @@ -35,7 +29,6 @@ export async function fetchRetry( return response; } } catch (error) { - clearTimeout(timeoutId); lastError = error; if (attempt === retries || error.name === 'AbortError') { From 1cc7498e76d90f2a3271544da9d3651753b0b2da Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sun, 28 Jul 2024 16:47:04 +0700 Subject: [PATCH 4/8] update github action to correctly install deno --- .github/workflows/publish.yaml | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 173d6d0..e9a9322 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -12,26 +12,26 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Clone repository + uses: actions/checkout@v3 - - name: Install Deno - run: | - curl -fsSL https://deno.land/x/install/install.sh | sh - echo "DENO_INSTALL=/home/runner/.deno" >> $GITHUB_ENV - echo "$DENO_INSTALL/bin" >> $GITHUB_PATH + - name: Install Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v1.x - - name: Run Deno build script - run: deno run -A build.ts + - name: Run Deno build script + run: deno run -A build.ts - - name: Navigate to npm directory - run: cd ./npm + - name: Navigate to npm directory + run: cd ./npm - - name: Setup .npmrc file to publish to npm - run: | - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc - env: - NPM_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} + - name: Setup .npmrc file to publish to npm + run: | + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc + env: + NPM_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} - - name: Publish to NPM - run: npm publish --dry-run - working-directory: ./npm + - name: Publish to NPM + run: npm publish --dry-run + working-directory: ./npm From a41305bdaa96d15562699024ba4dffaf07b4c3f0 Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sun, 28 Jul 2024 16:59:58 +0700 Subject: [PATCH 5/8] revert cheerio to npm dependency --- src/deps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deps.ts b/src/deps.ts index d495228..329789f 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,4 +1,4 @@ -import * as cheerio from 'https://cdn.skypack.dev/cheerio@1.0.0-rc.12?dts'; +import * as cheerio from 'npm:cheerio@1.0.0-rc.12'; import * as path from 'jsr:@std/path@1.0.1'; export { cheerio, path }; From 2ce924428bb19264c467ce334174664c4512bce5 Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sun, 28 Jul 2024 17:12:32 +0700 Subject: [PATCH 6/8] update readme with more devops --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3f47bd..9fabfb4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,10 @@ The SDK is distributed through: 3. Start scraping: 🚀 ```javascript +// node or bun: import { ScrapflyClient, ScrapeConfig } from 'scrapfly-sdk'; +// deno: +import { ScrapflyClient, ScrapeConfig } from 'jsr:@scrapfly/scrapfly-sdk'; const key = 'YOUR SCRAPFLY KEY'; const client = new ScrapflyClient({ key }); @@ -82,7 +85,7 @@ This is a Deno Typescript project that builds to NPM through [DNT](https://githu - `__tests__` directory contains tests for the source code. - `deno.json` contains meta information - `build.ts` is the build script that builds the project to nodejs ESM package. -- `/npm` directory will be produced when `buil.ts` is executed for building node package. +- `/npm` directory will be produced when `built.ts` is executed for building node package. ```bash # make modifications and run tests @@ -90,5 +93,11 @@ $ deno task test # format $ deno fmt # lint -$ deno linst +$ deno lint +# publish jsr: +$ deno publish +# build NPM package: +$ deno run -A build.ts +# publish NPM: +$ cd npm && npm publish ``` \ No newline at end of file From cd9bc12799fcac0be82e9a12d25bb2d21570ce9c Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Sun, 28 Jul 2024 17:25:54 +0700 Subject: [PATCH 7/8] update test github action --- .github/workflows/nodejs.yml | 20 -------------------- .github/workflows/run-tests.yml | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 20 deletions(-) delete mode 100644 .github/workflows/nodejs.yml create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index 1b35f0f..0000000 --- a/.github/workflows/nodejs.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Node.js CI - -on: - workflow_dispatch: - pull_request: - - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: volta-cli/action@v1 - - run: npm ci --no-audit - - run: npm run lint --if-present - - run: npm test - - run: npm run build --if-present - env: - CI: true diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..f202ae2 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,22 @@ +name: Run tests + +on: + workflow_dispatch: + pull_request: + + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@v3 + + - name: Install Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v1.x + + - name: Run Deno Test + run: deno task test From e5a1a9f1d8ca220b75ed8bb78f12b7468e17c462 Mon Sep 17 00:00:00 2001 From: granitosaurus Date: Mon, 29 Jul 2024 14:41:21 +0700 Subject: [PATCH 8/8] update github actions and clarify extraction compression error --- .github/workflows/publish.yaml | 2 +- .github/workflows/run-tests.yml | 6 ++++++ README.md | 4 ++-- deno.json | 2 ++ src/extractionconfig.ts | 3 +-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index e9a9322..52fd65b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -21,7 +21,7 @@ jobs: deno-version: v1.x - name: Run Deno build script - run: deno run -A build.ts + run: deno task build-npm - name: Navigate to npm directory run: cd ./npm diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f202ae2..ffc1f61 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -20,3 +20,9 @@ jobs: - name: Run Deno Test run: deno task test + + - name: Build JSR + run: deno task build-jsr + + - name: Build NPM + run: deno task build-npm \ No newline at end of file diff --git a/README.md b/README.md index 9fabfb4..3a568f5 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,10 @@ $ deno task test $ deno fmt # lint $ deno lint -# publish jsr: +# publish JSR: $ deno publish # build NPM package: -$ deno run -A build.ts +$ deno build-npm # publish NPM: $ cd npm && npm publish ``` \ No newline at end of file diff --git a/deno.json b/deno.json index 71f1f73..522809d 100644 --- a/deno.json +++ b/deno.json @@ -9,6 +9,8 @@ "tasks": { "start": "deno run --allow-net --allow-read src/main.ts", "test": "deno test --allow-net --allow-read __tests__", + "build-npm": "deno run -A build.ts", + "build-jsr": "deno publish --dry-run --allow-dirty", "fmt": "deno fmt" }, "fmt": { diff --git a/src/extractionconfig.ts b/src/extractionconfig.ts index 51bac22..d7c59be 100644 --- a/src/extractionconfig.ts +++ b/src/extractionconfig.ts @@ -120,8 +120,7 @@ export class ExtractionConfig { // } else { throw new errors.ExtractionConfigError( `Auto compression for ${this.document_compression_format} format isn't available. ` + - `You can manually compress to ${this.document_compression_format}` + - `or choose the gzip format for auto compression`, + `You can manually compress to ${this.document_compression_format}.`, ); // } }