diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 8596075..0000000 --- a/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -dist -.netlify -.vscode -docs diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index fe3251f..0000000 --- a/.eslintrc +++ /dev/null @@ -1,46 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2021, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - }, - "version": "detect" - }, - "settings": { - "react": { - "version": "detect" - } - }, - "plugins": ["unused-imports", "@typescript-eslint"], - "globals": { - "cy": "readonly", - "Cypress": "readonly", - "InferenceDocument": "readonly", - "TrainingInterface": "readonly" - }, - "env": { - "es2021": true, - "browser": true, - "node": true, - "jest": true, - "mocha": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended" - ], - "rules": { - "no-unused-vars": "off", - "unused-imports/no-unused-vars": "error", - "unused-imports/no-unused-imports": "error", - "react/prop-types": "off", - "no-redeclare": "off", - "@typescript-eslint/ban-ts-comment": "warn", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-non-null-asserted-optional-chain": "warn", - "@typescript-eslint/no-empty-function": "warn" - } -} diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/workflows/_static-analysis.yml b/.github/workflows/_static-analysis.yml new file mode 100644 index 0000000..3995ad3 --- /dev/null +++ b/.github/workflows/_static-analysis.yml @@ -0,0 +1,59 @@ +name: Static Analysis + +on: + workflow_call: + +permissions: + contents: read + +jobs: + static-check: + name: Run Static Analysis + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Run Betterleaks + id: betterleaks + continue-on-error: true + uses: dortort/betterleaks-action@cd0b905aa4befb8f42f8b1ab98150180c9e77515 # v0.1.0 + with: + version: 'v1.9.0' + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Betterleaks report + if: always() + uses: actions/upload-artifact@v7 + with: + name: betterleaks-report + path: betterleaks-report.sarif + if-no-files-found: warn + + - name: Fail if Betterleaks found leaks + if: steps.betterleaks.outcome == 'failure' + run: exit 1 + + - name: Set up pnpm + uses: pnpm/action-setup@v6 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Install Node.js dependencies + run: pnpm install --frozen-lockfile + + - name: Run static analysis + run: pnpm exec prek run --all-files --skip betterleaks + + - name: Lint + run: pnpm lint + + - name: Check dependency licenses + run: pnpm run license:check diff --git a/.github/workflows/_test-components.yml b/.github/workflows/_test-components.yml new file mode 100644 index 0000000..2232f91 --- /dev/null +++ b/.github/workflows/_test-components.yml @@ -0,0 +1,40 @@ +name: Component Tests + +on: + workflow_call: + +permissions: + contents: read + +jobs: + cypress: + name: Run Cypress component tests + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v7 + + - name: Set up pnpm + uses: pnpm/action-setup@v6 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Cypress run + uses: cypress-io/github-action@c541a95ea92d9239347a2355f2e895cd4b3f8060 # v7.4.6 + with: + install-command: pnpm install --frozen-lockfile + build: pnpm cy:prepare + command: pnpm cy:run + + - name: Upload snapshot diffs + if: failure() + uses: actions/upload-artifact@v7 + with: + name: cypress-snapshot-diffs + path: cypress/snapshots/**/__diff_output__ + if-no-files-found: ignore diff --git a/.github/workflows/_test-integration.yml b/.github/workflows/_test-integration.yml new file mode 100644 index 0000000..1836bf6 --- /dev/null +++ b/.github/workflows/_test-integration.yml @@ -0,0 +1,40 @@ +name: Integration Tests + +on: + workflow_call: + +permissions: + contents: read + +jobs: + cypress: + name: Run Cypress integration tests against the built library + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v7 + + - name: Set up pnpm + uses: pnpm/action-setup@v6 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: pnpm + + - name: Cypress run + uses: cypress-io/github-action@c541a95ea92d9239347a2355f2e895cd4b3f8060 # v7.4.6 + with: + install-command: pnpm install --frozen-lockfile + build: pnpm build + command: pnpm cy:run:integration + + - name: Upload screenshots + if: failure() + uses: actions/upload-artifact@v7 + with: + name: cypress-integration-screenshots + path: cypress/screenshots + if-no-files-found: ignore diff --git a/.github/workflows/_workflow-lint.yml b/.github/workflows/_workflow-lint.yml new file mode 100644 index 0000000..30b4773 --- /dev/null +++ b/.github/workflows/_workflow-lint.yml @@ -0,0 +1,28 @@ +name: Lint Workflows + +on: + workflow_call: + +permissions: + contents: read + +jobs: + actionlint: + name: Run actionlint + runs-on: ubuntu-latest + steps: + - name: Check out Git repository + uses: actions/checkout@v7 + + - name: Download actionlint + id: get_actionlint + # Script pinned to the v1.7.12 release commit; bump both together. + run: bash <(curl -fsSL "https://raw.githubusercontent.com/rhysd/actionlint/${ACTIONLINT_COMMIT}/scripts/download-actionlint.bash") "${ACTIONLINT_VERSION}" + shell: bash + env: + ACTIONLINT_VERSION: 1.7.12 + ACTIONLINT_COMMIT: 914e7df21a07ef503a81201c76d2b11c789d3fca + + - name: Run actionlint + run: '${{ steps.get_actionlint.outputs.executable }} -color' + shell: bash diff --git a/.github/workflows/deprecate-version.yml b/.github/workflows/deprecate-version.yml index 18e6df4..b5a5d9e 100644 --- a/.github/workflows/deprecate-version.yml +++ b/.github/workflows/deprecate-version.yml @@ -25,6 +25,9 @@ jobs: node-version-file: .nvmrc cache: pnpm registry-url: https://registry.npmjs.org - - run: pnpm deprecate $LIBRARY_NAME@${{ inputs.VERSION }} ${{ inputs.MESSAGE }} + - name: Deprecate version + run: pnpm deprecate "${LIBRARY_NAME}@${VERSION}" "${MESSAGE}" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + VERSION: ${{ inputs.VERSION }} + MESSAGE: ${{ inputs.MESSAGE }} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..ccc7b67 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,23 @@ +name: Pull Request + +on: + pull_request: + +permissions: + contents: read + pull-requests: read + +jobs: + workflow_lint: + uses: ./.github/workflows/_workflow-lint.yml + static_analysis: + uses: ./.github/workflows/_static-analysis.yml + needs: workflow_lint + test_components: + uses: ./.github/workflows/_test-components.yml + needs: static_analysis + secrets: inherit + test_integration: + uses: ./.github/workflows/_test-integration.yml + needs: static_analysis + secrets: inherit diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index c224d30..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Run tests on PRs - -on: - pull_request: - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - cache: pnpm - - name: install dependencies - run: pnpm install - - name: run lint - run: pnpm lint - - cypress-run: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - cache: pnpm - - name: Cypress run - uses: cypress-io/github-action@v6 - with: - install-command: pnpm install --frozen-lockfile - build: pnpm cy:prepare - command: pnpm cy:run diff --git a/.gitignore b/.gitignore index ffe3be1..fbf9898 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ cypress/snapshots/* cypress/**/__diff_output__ .idea .DS_Store + +# generated by Betterleaks +betterleaks-report.sarif diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..00acc11 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +dist +docs +node_modules +pnpm-lock.yaml +cypress/snapshots +CHANGELOG.md + +# generated by Betterleaks in CI +betterleaks-report.sarif diff --git a/.prettierrc b/.prettierrc index bc7735d..d1a7462 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,8 +3,9 @@ "tabWidth": 2, "singleQuote": true, "trailingComma": "all", - "semi": false, + "semi": true, "importOrder": [ + "", "^(react/(.*)$)|^(react$)", "", "", @@ -16,11 +17,6 @@ "", "^[./]" ], - "importOrderSeparation": false, - "importOrderSortSpecifiers": true, - "importOrderBuiltinModulesToTop": true, "importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"], - "importOrderMergeDuplicateImports": true, - "importOrderCombineTypeAndValueImports": true, "plugins": ["@ianvs/prettier-plugin-sort-imports"] } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index eba53b6..6d2f8d9 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # Contributor Covenant Code of Conduct ## Our Pledge @@ -18,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -107,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ad1dba..ab56acc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,20 +16,20 @@ Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). To help maintainers and the community to be efficient, follow these guidelines: -* **Use a clear and descriptive title** for the issue to identify the problem. -* **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, **don't just say what you did, but explain how you did it**. For example, with canvas related problem, explain if you used the mouse or a keyboard shortcut, what kind of image input it is etc... -* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). -* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. -* **Explain which behavior you expected to see instead and why.** -* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. -* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. +- **Use a clear and descriptive title** for the issue to identify the problem. +- **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, **don't just say what you did, but explain how you did it**. For example, with canvas related problem, explain if you used the mouse or a keyboard shortcut, what kind of image input it is etc... +- **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +- **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +- **Explain which behavior you expected to see instead and why.** +- **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. +- **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. > **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. Include details about your configuration and environment: -* **Which version of react-mindee-js are you using?** -* **What's the name and version of the browser you're using, on which OS**? +- **Which version of react-mindee-js are you using?** +- **What's the name and version of the browser you're using, on which OS**? ### Suggesting Enhancements @@ -37,13 +37,13 @@ Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com Make sure to provide the following information: -* **Use a clear and descriptive title** for the issue to identify the suggestion. -* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. -* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). -* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. -* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of the sdk which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. -* **Specify which version of react-mindee-js you're using.** -* **Specify the name and version of the browser and OS you're using.** +- **Use a clear and descriptive title** for the issue to identify the suggestion. +- **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +- **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +- **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of the sdk which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. +- **Specify which version of react-mindee-js you're using.** +- **Specify the name and version of the browser and OS you're using.** ### Pull Requests @@ -56,7 +56,7 @@ The process described here has several goals: Please follow these steps to have your contribution considered by the maintainers: -1. Follow all instructions in [the template](PULL_REQUEST_TEMPLATE.md) +1. Follow all instructions in [the template](.github/PULL_REQUEST_TEMPLATE.md) 2. Follow the [styleguides](#styleguides) While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. @@ -65,18 +65,18 @@ While the prerequisites above must be satisfied prior to having your pull reques ### Git Commit Messages -* Use the present tense ("Add feature" not "Added feature") -* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") -* Limit the first line to 72 characters or less -* Reference issues and pull requests liberally after the first line -* Please prefix your commit message with `chg:`, `new:` or `fix:` according to the content -* Consider starting the commit message with an applicable emoji, see [gitmoji](https://gitmoji.carloscuesta.me/) as a reference. +- Use the present tense ("Add feature" not "Added feature") +- Use the imperative mood ("Move cursor to..." not "Moves cursor to...") +- Limit the first line to 72 characters or less +- Reference issues and pull requests liberally after the first line +- Please prefix your commit message with `chg:`, `new:` or `fix:` according to the content +- Consider starting the commit message with an applicable emoji, see [gitmoji](https://gitmoji.carloscuesta.me/) as a reference. Example: -* chg: :pencil: Add usage section in README -* new: :sparkles: Add CONTRIBUTING file in repository -* fix: :bug: MyComponent - Prevent MouseEvent from firing unexpectedly +- chg: :pencil: Add usage section in README +- new: :sparkles: Add CONTRIBUTING file in repository +- fix: :bug: MyComponent - Prevent MouseEvent from firing unexpectedly ### JavaScript Styleguide diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index b11d248..25da814 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -10,8 +10,8 @@ Do you want to ask a question? Are you looking for enterprise support? Get in to Put an X between the brackets on this line if you have done all of the following: -* [ ] Reproduced the problem or exposed a new need -* [ ] Checked the github existing issues +- [ ] Reproduced the problem or exposed a new need +- [ ] Checked the github existing issues ### Description diff --git a/README.md b/README.md index 4e4077b..e0a61d7 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,9 @@ yarn add react-mindee-js You only need an image and a list of shapes to get started. ```jsx -import React from 'react' -import { AnnotationViewer } from 'react-mindee-js' - -import dummyImage from 'path-to-your/file.jpg' +import React from 'react'; +import dummyImage from 'path-to-your/file.jpg'; +import { AnnotationViewer } from 'react-mindee-js'; const dummyShapes = [ { @@ -68,15 +67,15 @@ const dummyShapes = [ [0.394, 0.087], ], }, -] +]; const data = { image: dummyImage, shapes: dummyShapes, -} +}; function App() { - return + return ; } ``` @@ -92,6 +91,74 @@ function App() { - **`style`** : style object to change container css properties - **`className`** : apply a className to the control +## PDF documents + +PDF pages are rendered in the browser with [pdf.js](https://mozilla.github.io/pdf.js/) (the first time a PDF is processed, it's loaded lazily in a Web Worker). +Pages are rendered by batch to accommodate very large documents. + +### `usePDFDocument` hook + +The simplest way to display a PDF in a React component: + +```jsx +import { useState } from 'react'; +import { AnnotationViewer, usePDFDocument } from 'react-mindee-js'; + +function Document({ file }) { + const { status, numPages, pages, loading, loadPage, error } = usePDFDocument( + file, + { prefetch: 5, batch: 3 }, + ); + const [current, setCurrent] = useState(0); + + if (status === 'error') return

{error.message}

; + if (status !== 'ready') return

Opening…

; + + return ( + <> + + {pages[current] && ( + + )} + + ); +} +``` + +### `openPDF` (framework-agnostic) + +For custom scheduling, use the handle directly. Page numbers are **1-based**, like pdf.js. + +```js +import { openPDF } from 'react-mindee-js'; + +const doc = await openPDF(file, { maxPages: 100 }); +doc.numPages; // 5 + +const first = await doc.getPage(1, { priority: 'high' }); // blob: URL +for await (const { pageNumber, image } of doc.getPages(2, 4)) { + // inclusive range, yielded as soon as each page is ready +} + +await doc.destroy(); // revokes every blob: URL handed out by this handle +``` + +### `getPDFPageCount` / `getImagesFromPDF` + +`getPDFPageCount(source)` returns the page count without rendering. `getImagesFromPDF` still works but is **deprecated**: it renders and holds every page in memory at once. Prefer `usePDFDocument` or `openPDF`. + ## Browser support React mindee supports all recent browsers and works where React works. However, you may need check the [SSR](/docs/ssr) section. diff --git a/cypress.config.ts b/cypress.config.ts index bd43a4a..e19975e 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,7 +1,6 @@ -import { defineConfig } from 'cypress' -import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin' -import { defineConfig as defineViteConfig } from 'vite' -import tsconfigPaths from 'vite-tsconfig-paths' +import { addMatchImageSnapshotPlugin } from '@simonsmith/cypress-image-snapshot/plugin'; +import { defineConfig } from 'cypress'; +import { defineConfig as defineViteConfig } from 'vite'; export default defineConfig({ viewportWidth: 1500, @@ -17,18 +16,29 @@ export default defineConfig({ // if (config.testingType === 'component') // on('dev-server:start', async (options) => startDevServer({ options })) - addMatchImageSnapshotPlugin(on, config) + addMatchImageSnapshotPlugin(on); - return config + return config; }, - specPattern: 'src/**/*.spec.{js,ts,jsx,tsx}', + specPattern: [ + 'src/**/*.spec.{js,ts,jsx,tsx}', + 'cypress/integration/**/*.spec.{ts,tsx}', + ], devServer: { framework: 'react', bundler: 'vite', viteConfig: defineViteConfig({ assetsInclude: ['**/*.tiff', '**/*.heic'], - plugins: [tsconfigPaths()], + resolve: { tsconfigPaths: true }, + optimizeDeps: { + include: [ + 'react', + 'react-dom', + 'react-dom/client', + 'react/jsx-runtime', + ], + }, }), }, }, -}) +}); diff --git a/cypress/.DS_Store b/cypress/.DS_Store deleted file mode 100644 index 1aa27bd..0000000 Binary files a/cypress/.DS_Store and /dev/null differ diff --git a/cypress/assets/.DS_Store b/cypress/assets/.DS_Store deleted file mode 100644 index e9cc0eb..0000000 Binary files a/cypress/assets/.DS_Store and /dev/null differ diff --git a/cypress/assets/shapes.ts b/cypress/assets/shapes.ts index 1f425af..771873f 100644 --- a/cypress/assets/shapes.ts +++ b/cypress/assets/shapes.ts @@ -1,4 +1,4 @@ -import { AnnotationShape } from '@/index' +import type { AnnotationShape } from '@/index'; export const dummyShapes: AnnotationShape[] = [ { @@ -46,4 +46,4 @@ export const dummyShapes: AnnotationShape[] = [ [0.625, 0.706], ], }, -] +]; diff --git a/cypress/integration/api.spec.ts b/cypress/integration/api.spec.ts new file mode 100644 index 0000000..a016afc --- /dev/null +++ b/cypress/integration/api.spec.ts @@ -0,0 +1,57 @@ +import { loadDist } from './helpers'; + +/** + * Guards the public runtime surface of the package. A missing or renamed + * export is a breaking change for consumers and must be deliberate. + */ +describe('built library public API', () => { + const expectedFunctions = [ + 'AnnotationLens', + 'AnnotationViewer', + 'dataURItoBlob', + 'drawLayer', + 'drawShape', + 'drawShapes', + 'getImagesFromPDF', + 'getPDFPageCount', + 'getZoomScale', + 'openPDF', + 'setShapeConfig', + 'toBase64', + 'usePDFDocument', + ]; + + it('exposes exactly the documented runtime exports, all of them functions', () => { + loadDist().then((lib) => { + expect(Object.keys(lib).sort()).to.deep.equal(expectedFunctions); + expectedFunctions.forEach((name) => { + expect(lib[name as keyof typeof lib]).to.be.a('function'); + }); + }); + }); + + it('builds data URLs with toBase64, defaulting to image/jpeg', () => { + loadDist().then(({ toBase64 }) => { + expect(toBase64('AAAA')).to.equal('data:image/jpeg;base64,AAAA'); + expect(toBase64('AAAA', 'application/pdf')).to.equal( + 'data:application/pdf;base64,AAAA', + ); + }); + }); + + it('round-trips a data URL through dataURItoBlob', () => { + loadDist().then(({ dataURItoBlob, toBase64 }) => { + const payload = btoa('hello'); + const blob = dataURItoBlob(toBase64(payload, 'image/png')); + expect(blob.type).to.equal('image/png'); + expect(blob.size).to.equal(5); + cy.wrap(blob.text()).should('equal', 'hello'); + }); + }); + + it('rejects malformed input in dataURItoBlob instead of producing an empty blob', () => { + loadDist().then(({ dataURItoBlob }) => { + expect(() => dataURItoBlob('not-a-data-url')).to.throw(); + }); + }); +}); diff --git a/cypress/integration/helpers.ts b/cypress/integration/helpers.ts new file mode 100644 index 0000000..cde28d5 --- /dev/null +++ b/cypress/integration/helpers.ts @@ -0,0 +1,64 @@ +import type * as Library from '@/index'; + +export type DistLibrary = typeof Library; + +/** + * Path of the built entry point, relative to this file. + */ +const distEntry = '../../dist/index.js'; + +export const CONTAINER_HEIGHT = 800; +export const CONTAINER_WIDTH = 700; +export const CONTAINER_STYLE = { + height: CONTAINER_HEIGHT, + width: CONTAINER_WIDTH, +}; + +/** + * Loads the built library so that it is kept between tests. + */ +export const loadDist = ( + options: { fresh?: boolean } = {}, +): Cypress.Chainable => { + const specifier = + options.fresh === true + ? `${distEntry}?fresh=${String(Date.now())}` + : distEntry; + return cy.wrap( + (import(/* @vite-ignore */ specifier) as Promise).catch( + (error: unknown) => { + throw new Error( + `Could not load dist/index.js, run "pnpm build" first. ${String(error)}`, + ); + }, + ), + ); +}; + +/** + * Used to prove that a lazy chunk is only + * downloaded once the feature that needs it is used. + */ +export const loadedResources = ( + win: Cypress.AUTWindow, + fragment: string, +): string[] => + win.performance + .getEntriesByType('resource') + .map((entry) => entry.name) + .filter((name) => name.includes(fragment)); + +/** Reads the natural dimensions of a data URL or object URL image. */ +export const imageDimensions = async ( + source: string, +): Promise<{ width: number; height: number }> => + await new Promise((resolve, reject) => { + const image = new Image(); + image.onload = (): void => { + resolve({ width: image.naturalWidth, height: image.naturalHeight }); + }; + image.onerror = (): void => { + reject(new Error('Image could not be decoded')); + }; + image.src = source; + }); diff --git a/cypress/integration/images.spec.tsx b/cypress/integration/images.spec.tsx new file mode 100644 index 0000000..01e0cd3 --- /dev/null +++ b/cypress/integration/images.spec.tsx @@ -0,0 +1,122 @@ +import dummyImageHEIC from 'cypress/assets/demo.heic'; +import dummyImage from 'cypress/assets/demo.jpg'; +import dummyImageTIFF from 'cypress/assets/demo.tiff'; +import multiPage from 'cypress/assets/multi-page.pdf'; + +import { CONTAINER_STYLE, loadDist, loadedResources } from './helpers'; + +const containerId = 'annotationViewer'; + +/** + * Asserts that the viewer ended up with a drawn image. + */ +const expectPaintedCanvas = (): void => { + cy.get(`#${containerId} canvas`) + .first() + .should(($canvas) => { + const canvas = $canvas[0] as HTMLCanvasElement; + const context = canvas.getContext('2d'); + if (context === null) { + throw new Error('Canvas has no 2d context'); + } + const { data } = context.getImageData(0, 0, canvas.width, canvas.height); + let opaquePixels = 0; + for (let index = 3; index < data.length; index += 4) { + if (data[index] !== 0) { + opaquePixels += 1; + } + } + expect(opaquePixels).to.be.greaterThan(0); + }); +}; + +/** + * Format conversion runs through utif (TIFF) and a lazily loaded heic2any + * chunk (HEIC). + */ +describe('built library image formats', () => { + it('displays a JPEG image', () => { + loadDist().then((lib) => { + cy.mount( + , + ); + expectPaintedCanvas(); + }); + }); + + it('displays a PNG data URL produced from a PDF page', () => { + loadDist().then((lib) => { + cy.wrap, string[]>( + lib.getImagesFromPDF(multiPage), + ).then((images) => { + cy.mount( + , + ); + expectPaintedCanvas(); + }); + }); + }); + + it('converts and displays a TIFF image', () => { + loadDist().then((lib) => { + cy.mount( + , + ); + expectPaintedCanvas(); + }); + }); + + it('converts and displays a HEIC image, downloading the heic2any chunk only then', () => { + loadDist().then((lib) => { + cy.mount( + , + ); + expectPaintedCanvas(); + cy.window().then((win) => { + expect(loadedResources(win, 'heic2any')).to.have.length(0); + }); + + cy.mount( + , + ); + expectPaintedCanvas(); + cy.window().should((win) => { + expect(loadedResources(win, 'heic2any')).to.have.length(1); + }); + }); + }); + + it('renders an empty stage without errors when no image is provided', () => { + loadDist().then((lib) => { + cy.mount( + , + ); + cy.get(`#${containerId}`).should('be.visible'); + cy.get(`#${containerId} canvas`).should('have.length.at.least', 1); + }); + }); +}); diff --git a/cypress/integration/lens.spec.tsx b/cypress/integration/lens.spec.tsx new file mode 100644 index 0000000..d92f7e4 --- /dev/null +++ b/cypress/integration/lens.spec.tsx @@ -0,0 +1,123 @@ +import { useState } from 'react'; +import dummyImage from 'cypress/assets/demo.jpg'; +import { dummyShapes } from 'cypress/assets/shapes'; +import type { Stage } from 'konva/lib/Stage'; + +import { CONTAINER_STYLE, loadDist, type DistLibrary } from './helpers'; + +const containerId = 'annotationLens'; + +const mountLens = ( + lib: DistLibrary, + props: { + zoomLevel?: number; + pointerPosition?: { x: number; y: number }; + } = {}, +): Cypress.Chainable<() => Stage> => { + let stage: Stage | undefined; + return cy + .mount( + { + stage = value; + }} + {...props} + />, + ) + .then(() => { + cy.get(`#${containerId} canvas`).should('have.length.at.least', 1); + }) + .then(() => (): Stage => { + if (stage === undefined) { + throw new Error('getStage was never called'); + } + return stage; + }); +}; + +describe('built library AnnotationLens', () => { + it('mounts with the image and every shape drawn', () => { + loadDist().then((lib) => { + mountLens(lib).then((stage) => { + cy.wrap(null).should(() => { + expect(stage().find('Line')).to.have.length(dummyShapes.length); + expect(stage().findOne('Image')).to.not.equal(undefined); + }); + }); + }); + }); + + it('scales the stage by the requested zoomLevel', () => { + loadDist().then((lib) => { + mountLens(lib, { zoomLevel: 2.5 }).then((stage) => { + cy.wrap(null).should(() => { + expect(stage().scaleX()).to.be.closeTo(2.5, 0.001); + expect(stage().scaleY()).to.be.closeTo(2.5, 0.001); + }); + }); + }); + }); + + it('moves the already mounted stage when the pointer position prop changes', () => { + loadDist().then((lib) => { + let stage: Stage | undefined; + const stageOrThrow = (): Stage => { + if (stage === undefined) { + throw new Error('getStage was never called'); + } + return stage; + }; + + /** + * Harness owning the pointer position as React state so that the test + * updates the prop of the same mounted lens instead of remounting it. + */ + const PointerHarness = (): React.JSX.Element => { + const [pointerPosition, setPointerPosition] = useState({ + x: 0.2, + y: 0.2, + }); + return ( + <> + + { + stage = value; + }} + /> + + ); + }; + + cy.mount(); + cy.wrap(null).should(() => { + expect(stageOrThrow().findOne('Image')).to.not.equal(undefined); + }); + cy.wrap(null).then(() => { + const initialStage = stageOrThrow(); + const initialPosition = initialStage.position(); + cy.get('[data-cy="move-pointer"]').click(); + cy.wrap(null).should(() => { + expect(stageOrThrow()).to.equal(initialStage); + const position = stageOrThrow().position(); + expect(position.x).to.not.equal(initialPosition.x); + expect(position.y).to.not.equal(initialPosition.y); + }); + }); + }); + }); +}); diff --git a/cypress/integration/pdf.spec.tsx b/cypress/integration/pdf.spec.tsx new file mode 100644 index 0000000..afdd892 --- /dev/null +++ b/cypress/integration/pdf.spec.tsx @@ -0,0 +1,160 @@ +import brokenPDF from 'cypress/assets/broken-pdf.pdf'; +import multiPage from 'cypress/assets/multi-page.pdf'; + +import { imageDimensions, loadDist } from './helpers'; + +const PAGE_COUNT = 5; + +/** + * Runs expectations against dist/ (rather than the sources). + */ +describe('built library PDF support', () => { + it('does not spawn the pdf.js worker until a PDF is actually processed', () => { + cy.window().then((win) => { + cy.spy(win, 'Worker').as('worker'); + }); + loadDist({ fresh: true }).then(({ getPDFPageCount }) => { + cy.get('@worker').should('not.have.been.called'); + cy.wrap(getPDFPageCount(multiPage)).should('equal', PAGE_COUNT); + }); + cy.get('@worker').should('have.been.calledOnce'); + }); + + it('spawns a real Web Worker from a blob URL instead of falling back to the main thread', () => { + cy.window().then((win) => { + cy.spy(win, 'Worker').as('worker'); + }); + loadDist({ fresh: true }).then(({ getPDFPageCount }) => { + cy.wrap(getPDFPageCount(multiPage)).should('equal', PAGE_COUNT); + }); + cy.get('@worker').should((spy) => { + expect(spy.calledWithNew()).to.equal(true); + expect(String(spy.firstCall.args[0])).to.match(/^blob:/); + }); + }); + + it('reuses a single worker across documents and calls', () => { + cy.window().then((win) => { + cy.spy(win, 'Worker').as('worker'); + }); + loadDist({ fresh: true }).then(({ getPDFPageCount, getImagesFromPDF }) => { + cy.wrap(getPDFPageCount(multiPage)).should('equal', PAGE_COUNT); + cy.wrap(getImagesFromPDF(multiPage)).should('have.length', PAGE_COUNT); + cy.wrap(getPDFPageCount(multiPage)).should('equal', PAGE_COUNT); + }); + cy.get('@worker').should('have.been.calledOnce'); + }); + + it('renders every page to a PNG data URL', () => { + loadDist().then(({ getImagesFromPDF }) => { + cy.wrap, string[]>(getImagesFromPDF(multiPage)).should( + (images) => { + expect(images).to.have.length(PAGE_COUNT); + images.forEach((image) => { + expect(image).to.match(/^data:image\/png;base64,/); + }); + }, + ); + }); + }); + + it('produces decodable images whose size follows the requested resolution', () => { + loadDist().then(({ getImagesFromPDF }) => { + type Dimensions = Awaited>; + const render = async (resolution: number): Promise => { + const images = await getImagesFromPDF( + multiPage, + Infinity, + undefined, + resolution, + ); + return await imageDimensions(images[0] ?? ''); + }; + cy.wrap, [Dimensions, Dimensions]>( + Promise.all([render(500_000), render(2_000_000)]), + ).should(([small, large]) => { + expect(small.width * small.height).to.be.greaterThan(0); + expect(large.width * large.height).to.be.greaterThan( + small.width * small.height, + ); + }); + }); + }); + + it('invokes onSuccess once the document is accepted, before pages resolve', () => { + const onSuccess = cy.spy().as('onSuccess'); + loadDist().then(({ getImagesFromPDF }) => { + cy.wrap(getImagesFromPDF(multiPage, Infinity, onSuccess)).should( + 'have.length', + PAGE_COUNT, + ); + }); + cy.get('@onSuccess').should('have.been.calledOnce'); + }); + + it('rejects with TooManyPagesError without invoking onSuccess when the page cap is exceeded', () => { + const onSuccess = cy.spy().as('onSuccess'); + loadDist().then(({ getImagesFromPDF }) => { + cy.wrap( + getImagesFromPDF(multiPage, PAGE_COUNT - 1, onSuccess).then( + () => 'resolved', + (error: unknown) => (error as Error).name, + ), + ).should('equal', 'TooManyPagesError'); + }); + cy.get('@onSuccess').should('not.have.been.called'); + }); + + it('rejects a broken PDF with InvalidPDFException from both entry points', () => { + loadDist().then(({ getImagesFromPDF, getPDFPageCount }) => { + cy.wrap( + getImagesFromPDF(brokenPDF).then( + () => 'resolved', + (error: unknown) => (error as Error).name, + ), + ).should('equal', 'InvalidPDFException'); + cy.wrap( + getPDFPageCount(brokenPDF).then( + () => 'resolved', + (error: unknown) => (error as Error).name, + ), + ).should('equal', 'InvalidPDFException'); + }); + }); + + it('keeps working after a failed document', () => { + loadDist().then(({ getImagesFromPDF, getPDFPageCount }) => { + cy.wrap(getImagesFromPDF(brokenPDF).catch(() => undefined)); + cy.wrap(getPDFPageCount(multiPage)).should('equal', PAGE_COUNT); + }); + }); + + it('renders pages on demand through openPDF with the inlined worker', () => { + cy.window().then((win) => { + cy.spy(win, 'Worker').as('worker'); + }); + loadDist({ fresh: true }).then(({ openPDF }) => { + cy.then(async () => { + const handle = await openPDF(multiPage); + const image = await handle.getPage(1); + const size = await imageDimensions(image); + await handle.destroy(); + return { numPages: handle.numPages, image, size }; + }).should(({ numPages, image, size }) => { + expect(numPages).to.equal(PAGE_COUNT); + expect(image).to.match(/^blob:/); + expect(size.width * size.height).to.be.greaterThan(0); + }); + }); + cy.get('@worker').should((spy) => { + expect(spy.calledOnce).to.equal(true); + expect(String(spy.firstCall.args[0])).to.match(/^blob:/); + }); + }); + + it('exposes usePDFDocument from the built entry point', () => { + loadDist().then(({ usePDFDocument }) => { + expect(usePDFDocument).to.be.a('function'); + }); + }); +}); diff --git a/cypress/integration/viewer.spec.tsx b/cypress/integration/viewer.spec.tsx new file mode 100644 index 0000000..f9a9fb9 --- /dev/null +++ b/cypress/integration/viewer.spec.tsx @@ -0,0 +1,368 @@ +import { useState } from 'react'; +import type { AnnotationShape, AnnotationViewerOptions } from '@/index'; +import dummyImage from 'cypress/assets/demo.jpg'; +import { dummyShapes } from 'cypress/assets/shapes'; +import type { Line } from 'konva/lib/shapes/Line'; +import type { Stage } from 'konva/lib/Stage'; + +import { CONTAINER_STYLE, loadDist, type DistLibrary } from './helpers'; + +const containerId = 'annotationViewer'; +const SHAPE_HIT = { x: 350, y: 50 }; +const EMPTY_AREA = { x: 10, y: 10 }; + +type MountResult = { stage: () => Stage }; + +/** + * Mounts the built AnnotationViewer with the demo image and shapes. + */ +const mountViewer = ( + lib: DistLibrary, + props: { + shapes?: AnnotationShape[]; + options?: AnnotationViewerOptions; + orientation?: 0 | 90 | 180 | 270; + customZoomLevel?: number; + onShapeClick?: (shape: AnnotationShape) => void; + onShapeMouseEnter?: (shape: AnnotationShape) => void; + onShapeMouseLeave?: (shape: AnnotationShape) => void; + onShapeMultiSelect?: (shapes: AnnotationShape[]) => void; + getPointerPosition?: (position: { x: number; y: number }) => void; + } = {}, +): Cypress.Chainable => { + let stage: Stage | undefined; + const { shapes = dummyShapes, orientation, ...rest } = props; + return cy + .mount( + { + stage = value; + }} + {...rest} + />, + ) + .then(() => { + cy.get(`#${containerId} canvas`).should('have.length.at.least', 1); + }) + .then(() => ({ + stage: (): Stage => { + if (stage === undefined) { + throw new Error('getStage was never called'); + } + return stage; + }, + })); +}; + +const waitForShapes = (stage: () => Stage, count: number): void => { + cy.wrap(null).should(() => { + expect(stage().find('Line')).to.have.length(count); + }); +}; + +describe('built library AnnotationViewer', () => { + it('draws one Konva.Line per shape, addressable by the shape id', () => { + loadDist().then((lib) => { + mountViewer(lib).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).should(() => { + dummyShapes.forEach((shape) => { + expect(stage().findOne(`#${shape.id}`)).to.not.equal(undefined); + }); + }); + }); + }); + }); + + it('redraws the existing stage when the shapes prop changes after mount', () => { + loadDist().then((lib) => { + let stage: Stage | undefined; + const stageOrThrow = (): Stage => { + if (stage === undefined) { + throw new Error('getStage was never called'); + } + return stage; + }; + + /** + * Harness owning the shapes as React state so that the test can push a + * new prop value into the same mounted viewer instead of remounting. + */ + const ShapesHarness = (): React.JSX.Element => { + const [shapes, setShapes] = useState(dummyShapes.slice(0, 2)); + return ( + <> + + { + stage = value; + }} + /> + + ); + }; + + cy.mount(); + waitForShapes(stageOrThrow, 2); + cy.wrap(null).then(() => { + const initialStage = stageOrThrow(); + cy.get('[data-cy="add-shapes"]').click(); + waitForShapes(stageOrThrow, dummyShapes.length); + cy.wrap(null).should(() => { + expect(stageOrThrow()).to.equal(initialStage); + }); + }); + }); + }); + + it('fires enter, click and leave callbacks with the matching shape', () => { + const onShapeClick = cy.spy().as('onShapeClick'); + const onShapeMouseEnter = cy.spy().as('onShapeMouseEnter'); + const onShapeMouseLeave = cy.spy().as('onShapeMouseLeave'); + loadDist().then((lib) => { + mountViewer(lib, { + onShapeClick, + onShapeMouseEnter, + onShapeMouseLeave, + }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + }); + }); + + cy.get(`#${containerId} .konvajs-content`).trigger( + 'mousemove', + SHAPE_HIT.x, + SHAPE_HIT.y, + ); + cy.get('@onShapeMouseEnter').should( + 'have.been.calledOnceWith', + dummyShapes[1], + ); + + cy.get(`#${containerId}`).click(SHAPE_HIT.x, SHAPE_HIT.y); + cy.get('@onShapeClick').should('have.been.calledOnceWith', dummyShapes[1]); + + cy.get(`#${containerId} .konvajs-content`).trigger( + 'mousemove', + EMPTY_AREA.x, + EMPTY_AREA.y, + ); + cy.get('@onShapeMouseLeave').should( + 'have.been.calledOnceWith', + dummyShapes[1], + ); + }); + + it('reports relative pointer positions within [0, 1] while hovering the image', () => { + const getPointerPosition = cy.spy().as('getPointerPosition'); + loadDist().then((lib) => { + mountViewer(lib, { getPointerPosition }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + }); + }); + cy.get(`#${containerId} .konvajs-content`).trigger( + 'mousemove', + SHAPE_HIT.x, + SHAPE_HIT.y, + ); + cy.get('@getPointerPosition').should((spy) => { + expect(spy.called).to.equal(true); + const { x, y } = spy.lastCall.args[0] as { x: number; y: number }; + expect(x).to.be.within(0, 1); + expect(y).to.be.within(0, 1); + }); + }); + + it('does not create a selection rectangle unless enableSelection is set', () => { + loadDist().then((lib) => { + mountViewer(lib).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.get(`#${containerId}`) + .children() + .trigger('keydown', { altKey: true, ctrlKey: true }) + .trigger('mousedown', { which: 1, clientX: 10, clientY: 10 }) + .trigger('mousemove', { which: 1, clientX: 600, clientY: 300 }); + cy.wrap(null).should(() => { + expect(stage().find('Rect')).to.have.length(0); + }); + cy.get(`#${containerId}`) + .trigger('mouseup') + .trigger('keyup', { altKey: true, ctrlKey: true }); + }); + }); + }); + + it('reports the shapes enclosed by a selection rectangle when enableSelection is set', () => { + const onShapeMultiSelect = cy.spy().as('onShapeMultiSelect'); + loadDist().then((lib) => { + mountViewer(lib, { + options: { enableSelection: true }, + onShapeMultiSelect, + }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + }); + }); + cy.get(`#${containerId}`) + .children() + .trigger('keydown', { altKey: true, ctrlKey: true }) + .trigger('mousedown', { which: 1, clientX: 10, clientY: 10 }) + .trigger('mousemove', { which: 1, clientX: 600, clientY: 300 }); + cy.get(`#${containerId}`) + .trigger('mouseup') + .trigger('keyup', { altKey: true, ctrlKey: true }); + cy.get('@onShapeMultiSelect').should( + 'have.been.calledOnceWithExactly', + dummyShapes.slice(0, 2), + ); + }); + + it('zooms in on wheel up, exposes the level through getZoomScale and respects the max', () => { + loadDist().then((lib) => { + mountViewer(lib, { + options: { zoom: { modifier: 1.5, max: 2, defaultZoom: 1 } }, + }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).should(() => { + expect(lib.getZoomScale(stage())).to.equal(1); + }); + cy.get(`#${containerId}`).trigger('wheel', { deltaY: -60 }); + cy.wrap(null).should(() => { + expect(lib.getZoomScale(stage())).to.be.closeTo(1.5, 0.01); + }); + cy.get(`#${containerId}`) + .trigger('wheel', { deltaY: -60 }) + .trigger('wheel', { deltaY: -60 }); + cy.wrap(null).should(() => { + expect(lib.getZoomScale(stage())).to.be.at.most(2); + }); + cy.get(`#${containerId}`).trigger('wheel', { deltaY: 60 }); + cy.wrap(null).should(() => { + expect(lib.getZoomScale(stage())).to.be.lessThan(2); + }); + }); + }); + }); + + it('applies customZoomLevel on mount', () => { + loadDist().then((lib) => { + mountViewer(lib, { customZoomLevel: 1.75 }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).should(() => { + expect(lib.getZoomScale(stage())).to.be.closeTo(1.75, 0.01); + }); + }); + }); + }); + + it('swaps the displayed image aspect ratio for a 90 degree orientation', () => { + const imageRatio = (stage: Stage): number => { + const image = stage.findOne('Image'); + if (image === undefined) { + throw new Error('No image node on stage'); + } + return image.width() / image.height(); + }; + loadDist().then((lib) => { + let uprightRatio = 0; + mountViewer(lib, { orientation: 0 }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).should(() => { + uprightRatio = imageRatio(stage()); + expect(uprightRatio).to.be.greaterThan(0); + }); + }); + mountViewer(lib, { orientation: 90 }).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).should(() => { + expect(imageRatio(stage())).to.be.closeTo(1 / uprightRatio, 0.05); + }); + }); + }); + }); + + it('lets consumers restyle shapes through setShapeConfig, drawShape and drawShapes', () => { + loadDist().then((lib) => { + mountViewer(lib).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.wrap(null).then(() => { + const target = dummyShapes[0]; + if (target === undefined) { + throw new Error('Missing fixture shape'); + } + lib.setShapeConfig(stage(), target.id, { stroke: 'green' }); + expect(stage().findOne(`#${target.id}`)?.stroke()).to.equal( + 'green', + ); + + lib.drawShape(stage(), target.id, { stroke: 'blue' }); + expect(stage().findOne(`#${target.id}`)?.stroke()).to.equal( + 'blue', + ); + + lib.drawShapes(stage(), { strokeWidth: 7 }); + stage() + .find('Line') + .forEach((line) => { + expect(line.strokeWidth()).to.equal(7); + }); + }); + }); + }); + }); + + it('logs an error rather than throwing when styling an unknown shape id', () => { + loadDist().then((lib) => { + mountViewer(lib).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + cy.window().then((win) => { + const consoleError = cy.spy(win.console, 'error'); + lib.setShapeConfig(stage(), 'does-not-exist', { stroke: 'red' }); + lib.drawShape(stage(), 'does-not-exist', { stroke: 'red' }); + expect(consoleError.callCount).to.equal(2); + }); + }); + }); + }); + + it('warns and skips malformed coordinates instead of breaking the whole layer', () => { + const malformed: AnnotationShape = { + id: 'malformed', + coordinates: [[0.1, 0.1], [0.2], [0.2, 0.2, 0.3], [0.1, 0.2]], + }; + loadDist().then((lib) => { + cy.window().then((win) => { + cy.spy(win.console, 'warn').as('consoleWarn'); + }); + mountViewer(lib, { shapes: [...dummyShapes, malformed] }).then( + ({ stage }) => { + waitForShapes(stage, dummyShapes.length + 1); + cy.get('@consoleWarn').should('have.been.called'); + }, + ); + }); + }); + + it('removes its canvases from the DOM on unmount', () => { + loadDist().then((lib) => { + mountViewer(lib).then(({ stage }) => { + waitForShapes(stage, dummyShapes.length); + }); + cy.mount(
); + cy.get('[data-cy="placeholder"]').should('exist'); + cy.get('canvas').should('have.length', 0); + }); + }); +}); diff --git a/cypress/support/component-index.html b/cypress/support/component-index.html index ac6e79f..faf3b5f 100644 --- a/cypress/support/component-index.html +++ b/cypress/support/component-index.html @@ -1,12 +1,12 @@ - + - - - + + + Components App
- \ No newline at end of file + diff --git a/cypress/support/component.ts b/cypress/support/component.ts index e7c2d99..4a3515d 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -1,9 +1,9 @@ -import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command' -import { mount } from 'cypress/react18' +import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command'; +import { mount } from 'cypress/react'; addMatchImageSnapshotCommand({ failureThreshold: 0.2, failureThresholdType: 'percent', capture: 'runner', -}) -Cypress.Commands.add('mount', mount) +}); +Cypress.Commands.add('mount', mount); diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts index 507e83f..ed8caa9 100644 --- a/cypress/support/index.d.ts +++ b/cypress/support/index.d.ts @@ -1,9 +1,9 @@ -import { mount } from 'cypress/react18' +import type { mount } from 'cypress/react'; declare global { namespace Cypress { interface Chainable { - mount: typeof mount + mount: typeof mount; } } } diff --git a/docs/docs/API/annotation-viewer-api.mdx b/docs/docs/API/annotation-viewer-api.mdx index d726353..ff7a868 100644 --- a/docs/docs/API/annotation-viewer-api.mdx +++ b/docs/docs/API/annotation-viewer-api.mdx @@ -28,7 +28,7 @@ type AnnotationShape = { } ``` -> **image** property should be either an URL object or a base64. If you have an object of type [File](https://developer.mozilla.org/fr/docs/Web/API/File) ( typically generated by file upload libraries) instead you may need to use [URL.createObjectURL()](https://developer.mozilla.org/fr/docs/Web/API/URL/createObjectURL) to generate a valid URL object. +> **image** property should be either an URL (including `blob:` object URLs) or a base64 data URL. If you have an object of type [File](https://developer.mozilla.org/fr/docs/Web/API/File) (typically generated by file upload libraries), you may need to use [URL.createObjectURL()](https://developer.mozilla.org/fr/docs/Web/API/URL/createObjectURL) to generate a valid URL object instead. JPEG, PNG, TIFF (first page) and HEIC/HEIF (primary image) are supported; TIFF and HEIF are converted in the browser, the HEIF decoder being downloaded on first use. For PDF files, see [usePDFDocument](/docs/Utils/use-pdf-document). :::caution @@ -54,9 +54,9 @@ interface AnnotationViewerOptions { onMouseLeave?: (polygon: Line) => void onClick?: (polygon: Line) => void zoom?: { - modifier: number - max: number - defaultZoom: number + modifier: number // multiplier applied per wheel step + max: number // highest magnification, relative to the fitted image (1 = fits the viewer) + defaultZoom: number // accepted for backward compatibility, currently unused } } ``` diff --git a/docs/docs/Tutorial/pdf-file.md b/docs/docs/Tutorial/pdf-file.md index fb060e2..eba272c 100644 --- a/docs/docs/Tutorial/pdf-file.md +++ b/docs/docs/Tutorial/pdf-file.md @@ -4,17 +4,54 @@ sidebar_position: 2 # PDF file -> If you got a PDF file in hand, you can use [getImagesFromPDF](/docs/Utils/get-images-form-pdf-api) to extract pages in base64 format and pass them to `` using a proper state. +> PDF pages are rendered in the browser on demand. Use the [usePDFDocument](/docs/Utils/use-pdf-document) hook to display a document progressively: the first pages are rendered right away, the rest only when the user reaches them. ```tsx -getImagesFromPDF(pdfFile).then((images: string[]) => { - console.log(images) // ===> [base64_1, base64_2, ...] -}) +import { useState } from 'react' +import { AnnotationViewer, usePDFDocument } from 'react-mindee-js' + +function PdfViewer({ file }: { file: File }) { + const { status, numPages, pages, loading, loadPage } = usePDFDocument(file) + const [current, setCurrent] = useState(0) + + const goTo = (index: number) => { + setCurrent(index) + loadPage(index) // renders the page and the rest of its batch if needed + } + + if (status !== 'ready') return

{status}

+ + return ( + <> + + + {current + 1} / {numPages} + + + + {pages[current] ? ( + + ) : ( +

{loading.has(current) ? 'Rendering…' : 'Not loaded'}

+ )} + + ) +} ``` -### Full example +Outside React, or for custom scheduling, use [openPDF](/docs/Utils/open-pdf) directly: + +```ts +const doc = await openPDF(file) +const firstPage = await doc.getPage(1) +// ... +await doc.destroy() +``` - +:::caution +Page images are `blob:` object URLs that live until the document is destroyed. If you need to send one to a server, read it back with `fetch(url).then((r) => r.blob())` first. +::: diff --git a/docs/docs/Utils/draw-layer.mdx b/docs/docs/Utils/draw-layer.mdx index b5f75f0..af0d9ee 100644 --- a/docs/docs/Utils/draw-layer.mdx +++ b/docs/docs/Utils/draw-layer.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 7 --- # drawLayer diff --git a/docs/docs/Utils/draw-shape.mdx b/docs/docs/Utils/draw-shape.mdx index 2a55804..35672f4 100644 --- a/docs/docs/Utils/draw-shape.mdx +++ b/docs/docs/Utils/draw-shape.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 5 --- # drawShape diff --git a/docs/docs/Utils/get-images-form-pdf-api.mdx b/docs/docs/Utils/get-images-form-pdf-api.mdx index 4dd0e30..d893c2e 100644 --- a/docs/docs/Utils/get-images-form-pdf-api.mdx +++ b/docs/docs/Utils/get-images-form-pdf-api.mdx @@ -1,15 +1,23 @@ --- -sidebar_position: 1 +sidebar_position: 3 --- -# getImageFromPDF +# getImagesFromPDF -- Type : `(file: string) => Promise` +:::caution Deprecated +`getImagesFromPDF` renders and holds **every** page in memory at once. Use [openPDF](/docs/Utils/open-pdf) or the [usePDFDocument](/docs/Utils/use-pdf-document) hook to render pages progressively instead. +::: -> The parameter should be a valid Object URL of type string. If you have an object of type [File](https://developer.mozilla.org/fr/docs/Web/API/File) ( typically generated by file upload libraries) in hand you must use [URL.createObjectURL()](https://developer.mozilla.org/fr/docs/Web/API/URL/createObjectURL) to generate a valid URL object. +- Type : `(source: PDFSource, maxPages?: number, onSuccess?: () => void, resolution?: number) => Promise` + +> Renders every page of a PDF to a PNG data URL. Pages that fail to render are skipped, so the result may be shorter than the page count. `source` accepts a URL string, a `File`/`Blob`, an `ArrayBuffer` or a `Uint8Array`. ```javascript getImagesFromPDF(file).then((images) => { - // Do something with images pdf pages + // images: ['data:image/png;base64,...', ...] }) ``` + +- **`maxPages`** : rejects with a `TooManyPagesError` when the document has more pages. +- **`onSuccess`** : called once the document has been accepted, before pages are rendered. +- **`resolution`** : target pixel count of each page, see [PDFPageOptions](/docs/Utils/open-pdf#page-options). diff --git a/docs/docs/Utils/get-pdf-page-count.mdx b/docs/docs/Utils/get-pdf-page-count.mdx index 4d2c976..e6caca7 100644 --- a/docs/docs/Utils/get-pdf-page-count.mdx +++ b/docs/docs/Utils/get-pdf-page-count.mdx @@ -1,15 +1,20 @@ --- -sidebar_position: 2 +sidebar_position: 4 --- # getPDFPageCount -- Type : `(file: string) => Promise` +- Type : `(source: PDFSource) => Promise` +- `PDFSource` : `string | Blob | ArrayBuffer | Uint8Array` -> The parameter should be a valid Object URL of type string. If you have an object of type [File](https://developer.mozilla.org/fr/docs/Web/API/File) ( typically generated by file upload libraries) in hand you must use [URL.createObjectURL()](https://developer.mozilla.org/fr/docs/Web/API/URL/createObjectURL) to generate a valid URL object. +> Returns the number of pages without rendering anything. ```javascript getPDFPageCount(file).then((pageCount) => { // Do something with the page count }) ``` + +:::tip +If you also need to render pages, call [openPDF](/docs/Utils/open-pdf) once and read `numPages` from the handle so the document is only parsed once. +::: diff --git a/docs/docs/Utils/open-pdf.mdx b/docs/docs/Utils/open-pdf.mdx new file mode 100644 index 0000000..5ab9cd6 --- /dev/null +++ b/docs/docs/Utils/open-pdf.mdx @@ -0,0 +1,102 @@ +--- +sidebar_position: 1 +--- + +# openPDF + +- Type : `(source: PDFSource, options?: OpenPDFOptions) => Promise` + +> Parses a PDF once and returns a handle from which pages are rendered **on demand**. Only the pages you ask for are rasterized, the requested one first, and memory is released by `destroy()`. This is the primitive behind [usePDFDocument](/docs/Utils/use-pdf-document) and the recommended way to display documents with more than a handful of pages. + +pdf.js and its Web Worker are downloaded lazily, the first time a PDF is processed, and a single worker is shared by every document in the tab. + +```ts +import { openPDF } from 'react-mindee-js' + +const doc = await openPDF(file, { maxPages: 100 }) +doc.numPages // 5 + +const first = await doc.getPage(1, { priority: 'high' }) // blob: URL +for await (const { pageNumber, image } of doc.getPages(2, 4)) { + // inclusive range, yielded as soon as each page is ready +} + +await doc.destroy() // revokes every blob: URL handed out by this handle +``` + +## Source + +`PDFSource = string | Blob | ArrayBuffer | Uint8Array` + +A URL or data URL string, a [File](https://developer.mozilla.org/docs/Web/API/File)/`Blob` (typically from a file input or upload library), or raw bytes. You no longer need `URL.createObjectURL()` to pass a `File`. + +## Options + +```ts +interface OpenPDFOptions { + /** Rejects with a `TooManyPagesError` when the document has more pages. */ + maxPages?: number + /** Aborting stops the download/parse and rejects with an `AbortError`. */ + signal?: AbortSignal +} +``` + +## Handle + +```ts +interface PDFDocumentHandle { + readonly numPages: number + /** Renders a single page. `pageNumber` is 1-based, like pdf.js. */ + getPage: (pageNumber: number, options?: PDFPageOptions) => Promise + /** Renders pages `from` to `to` inclusive (1-based) one after the other, yielding each as soon as it is ready. */ + getPages: (from: number, to: number, options?: PDFPageOptions) => AsyncIterable + /** Cancels pending renders, revokes every object URL and releases the pdf.js document. */ + destroy: () => Promise +} + +interface PDFRenderedPage { + pageNumber: number // 1-based + image: string +} +``` + +:::caution +Page numbers are **1-based**. `getPage(0)`, a page above `numPages` or an inverted range reject with a `RangeError` before anything is rendered. +::: + +## Page options + +```ts +interface PDFPageOptions { + resolution?: number // default 1 500 000 px (width × height), must be > 0 + output?: 'object-url' | 'data-url' // default 'object-url' + priority?: 'high' | 'low' // default 'high' for getPage, 'low' for getPages + signal?: AbortSignal +} +``` + +- **`resolution`** : target pixel count of the render. The viewer displays 1.5 Mpx; use a much lower value such as `50_000` for thumbnails. Renders at different resolutions are cached independently. +- **`output`** : `object-url` returns a `blob:` URL backed by a PNG blob, which is light to hold in memory and is revoked by `destroy()`. `data-url` returns a self-contained base64 PNG string. +- **`priority`** : `high` puts the request ahead of everything still waiting, so a page the user explicitly asked for is drawn before background prefetches. Requesting an already queued page again with `high` promotes it. +- **`signal`** : aborting removes the request from the queue (or cancels the in-progress render) and rejects **that caller's** promise with an `AbortError`. Other callers waiting on the same page are unaffected; the render itself is only cancelled once nobody waits for it. + +## Behaviour + +- Pages render one at a time per document, in priority order. Every successful render is cached per page, resolution and output for the lifetime of the handle. +- Several calls for the same page share one render. +- `getPages` requests one page at a time, so breaking out of the `for await` loop leaves nothing pending. +- After `destroy()`, every pending call rejects (even a render that was about to complete) and the handle is unusable. + +:::caution +Images are `blob:` object URLs by default: they only exist in the current tab and only until `destroy()`. To upload one, read it back first (`await fetch(url).then((r) => r.blob())`) or request `{ output: 'data-url' }`. +::: + +## Errors + +| `error.name` | Cause | +| :-------------------- | :------------------------------------------------------ | +| `InvalidPDFException` | The source is not a readable PDF | +| `TooManyPagesError` | The document has more pages than `maxPages` | +| `RangeError` | A page number or range is outside `1..numPages` | +| `AbortError` | The request's `signal` was aborted | +| `Error` | A render was requested after `destroy()` | diff --git a/docs/docs/Utils/set-shape-config.mdx b/docs/docs/Utils/set-shape-config.mdx index 57d0840..e6a4299 100644 --- a/docs/docs/Utils/set-shape-config.mdx +++ b/docs/docs/Utils/set-shape-config.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 6 --- # setShapeConfig diff --git a/docs/docs/Utils/use-pdf-document.mdx b/docs/docs/Utils/use-pdf-document.mdx new file mode 100644 index 0000000..39918ef --- /dev/null +++ b/docs/docs/Utils/use-pdf-document.mdx @@ -0,0 +1,105 @@ +--- +sidebar_position: 2 +--- + +# usePDFDocument + +- Type : `(source: PDFSource | null | undefined, options?: UsePDFDocumentOptions) => UsePDFDocumentResult` + +> React hook that opens a PDF with [openPDF](/docs/Utils/open-pdf) and renders its pages progressively: the first `prefetch` pages right away, then `batch` pages at a time whenever `loadPage` is called for a page that is not rendered yet. The document and every image URL it produced are released automatically when `source` changes or the component unmounts. + +```tsx +import { useState } from 'react' +import { AnnotationViewer, usePDFDocument } from 'react-mindee-js' + +function Document({ file }: { file: File }) { + const { status, pages, loading, loadPage, error } = usePDFDocument(file, { + prefetch: 5, + batch: 3, + }) + const [current, setCurrent] = useState(0) + + if (status === 'error') return

{error?.message}

+ if (status !== 'ready') return

Opening…

+ + return ( + <> + + {pages[current] && ( + + )} + + ) +} +``` + +## Options + +```ts +interface UsePDFDocumentOptions { + prefetch?: number // default 5, integer >= 0 + batch?: number // default 3, integer >= 1 + resolution?: number // see PDFPageOptions.resolution + maxPages?: number // see OpenPDFOptions.maxPages +} +``` + +- **`prefetch`** : pages rendered as soon as the document opens. +- **`batch`** : pages rendered together when `loadPage` asks for one that is not rendered yet. The first `prefetch` pages form one block, the remaining pages are grouped `batch` at a time after it. + +## Result + +```ts +interface UsePDFDocumentResult { + status: 'idle' | 'opening' | 'ready' | 'error' + error: Error | null + numPages: number + pages: (string | undefined)[] + loading: ReadonlySet + loadPage: (index: number) => void + loadRange: (from: number, to: number) => void + document: PDFDocumentHandle | null +} +``` + +- **`pages`** : one slot per page, **0-based**. A slot is `undefined` until the page has been rendered, then holds an image URL usable as `data.image`. +- **`loading`** : indexes whose render is queued or in progress, handy for per-page skeletons. +- **`loadPage(index)`** : ensures the page and the rest of its batch are rendered. Idempotent and cheap, so call it liberally (on scroll, click, keyboard navigation…). The page itself is requested with high priority so it is drawn before any pending prefetch, even if it was already queued. +- **`loadRange(from, to)`** : renders every page in `[from, to)` with low priority. Bounds are clamped to the document; non-integer indexes (here and in `loadPage`) are ignored. +- **`document`** : the underlying handle for advanced use, `null` until `status` is `ready`. + +:::caution +The hook is **0-based** (array semantics); the handle is **1-based** (pdf.js semantics). Do not mix them up when using `document` directly. +::: + +:::tip +Keep `source` referentially stable between renders (store the `File` in state, do not rebuild a data URL on every render), otherwise the hook closes and reopens the document each time. Changing `maxPages` also reopens the document. A document still downloading or parsing when the source changes (or the component unmounts) is aborted immediately. Changing `resolution` keeps the document but clears `pages`, aborts any render still pending at the old value, and re-renders every page that was loaded (or loading) at the new value (renders are cached per resolution, so switching back is instant). +::: + +### Example: load pages as they scroll into view + +```tsx +const { pages, loadPage } = usePDFDocument(file) + +useEffect(() => { + const observer = new IntersectionObserver((entries) => { + entries + .filter((entry) => entry.isIntersecting) + .forEach((entry) => loadPage(Number(entry.target.dataset.index))) + }) + document.querySelectorAll('[data-index]').forEach((el) => observer.observe(el)) + return () => observer.disconnect() +}, [pages.length, loadPage]) +``` diff --git a/docs/docs/customization.md b/docs/docs/customization.md index 92a64ef..ce0ec2e 100644 --- a/docs/docs/customization.md +++ b/docs/docs/customization.md @@ -24,7 +24,7 @@ sidebar_position: 6 individual shape [**config**](https://konvajs.org/api/Konva.Shape.html) passed on each item of the list of shapes override the **shapeConfig** property ::: -- `zoom`: +- `zoom`: wheel zoom settings. `modifier` is the multiplier applied per wheel step; `max` is the highest magnification reachable (`1` = fits, `3` = three times larger), independently of the image pixel size. `defaultZoom` is accepted for backward compatibility but currently has no effect. ##### Example diff --git a/docs/docs/intro.md b/docs/docs/intro.md index 2a1aabb..7098bd5 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -16,7 +16,7 @@ This library was made for building frontend interfaces on top of **[Mindee](http ## Features -- Support for image and PDF files +- Support for image (JPEG, PNG, TIFF, HEIC/HEIF) and PDF files, with progressive page rendering for PDFs - Interactive shapes with events binding - Extensible styling API - Controllable state props and modular architecture @@ -25,20 +25,20 @@ This library was made for building frontend interfaces on top of **[Mindee](http ## Compatibility -The React SDK is compatible with `React 16.8.0 +` +The React SDK is compatible with `React 18` and `React 19`, and requires `Node.js >= 22.13` to build. ## Installation and dependencies The easiest way to use react-select is to install it from npm and build it into your app with Webpack. ```bash -npm install --save react-mindee-js@1.3.0 +npm install --save react-mindee-js ``` or using yarn ``` -yarn add react-mindee-js@1.3.0 +yarn add react-mindee-js ``` ## Usage @@ -94,6 +94,10 @@ function App() { - **`style`** : style object to change container css properties - **`className`** : apply a className to the control +## PDF documents + +PDF pages are rendered on demand with the [usePDFDocument](/docs/Utils/use-pdf-document) hook or the lower-level [openPDF](/docs/Utils/open-pdf) handle. See the [PDF tutorial](/docs/Tutorial/pdf-file). + ## Browser support React mindee supports all recent browsers and works where React works. However, you may need check the [SSR](/docs/ssr) section. @@ -103,8 +107,8 @@ React mindee supports all recent browsers and works where React works. However, Feel free to use github to submit issues, pull requests or general feedback. You can also visit [our website](https://mindee.com) or drop us an [email](mailto:contact@mindee.com). -Please read our [Contributing section](https://github.com/publicMindee/react-mindee-js/blob/master/CONTRIBUTING.md) before contributing. +Please read our [Contributing section](https://github.com/mindee/react-mindee-js/blob/master/CONTRIBUTING.md) before contributing. ## License -GPLv3 © [mindee](https://mindee.com) +MIT © [mindee](https://mindee.com) diff --git a/docs/docs/migration.md b/docs/docs/migration.md index fff6dd6..c4403c5 100644 --- a/docs/docs/migration.md +++ b/docs/docs/migration.md @@ -2,7 +2,39 @@ sidebar_position: 5 --- -# Migration from v1.2 to v1.3 +# Migration + +## From v1.x to v2.0 + +### Requirements + +- `Node.js >= 22.13` to build. The published package targets modern browsers and is compatible with React 18 and 19. +- The package is ESM only. pdf.js and the HEIC decoder are loaded lazily as separate chunks, so your bundler must support dynamic `import()` (every mainstream bundler does). + +### `zoom.max` is now relative to the fitted image + +Previously `options.zoom.max` was compared against the absolute canvas scale, so the reachable magnification depended on the pixel size of the image. It is now a factor relative to the image **fitted in the container** (`1` = fits). If you relied on the old behaviour, lower large values (a `max` of `10` now means "ten times the fitted size"). + +### PDF helpers + +| v1 | v2 | +|:---------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------| +| `getImagesFromPDF(url)` renders every page | **Deprecated.** Use [usePDFDocument](/docs/Utils/use-pdf-document) or [openPDF](/docs/Utils/open-pdf) to render on demand | +| `getImagesFromPDF(url, maxPages, onSuccess)` | Still supported, now also accepts a `File`/`Blob`/`ArrayBuffer` and a trailing `resolution` | +| `getPDFPageCount(url)` | Unchanged, also accepts a `File`/`Blob`/`ArrayBuffer` | +| URL string required | Any `PDFSource`; `URL.createObjectURL()` is no longer needed for a `File` | +| Pages returned as base64 data URLs | New APIs return `blob:` object URLs by default (lighter, revoked on `destroy()`); pass `{ output: 'data-url' }` for base64 | + +### Image formats + +- HEIC/HEIF files are now detected by every HEIF MIME type (`image/heic`, `image/heif` and their `-sequence` variants); previously only `image/heic` was converted. +- The HEIC decoder (`heic2any`) is loaded lazily, only when such a file is displayed. + +### Removed + +- `loadPdfDocument` (internal pdf.js loader) is no longer exported. Use `openPDF`. + +## From v1.2 to v1.3 > Version 1.3 API becomes simpler. Here is the changes : diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..4d835d3 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,136 @@ +import js from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import reactHooks from 'eslint-plugin-react-hooks'; +import { defineConfig } from 'eslint/config'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default defineConfig( + { ignores: ['dist', 'docs', 'node_modules', 'cypress/snapshots'] }, + js.configs.recommended, + tseslint.configs.strictTypeChecked, + tseslint.configs.stylisticTypeChecked, + reactHooks.configs.flat['recommended-latest'], + { + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, + languageOptions: { + globals: { ...globals.browser, ...globals.es2022 }, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + // --- typing strictness (beyond strictTypeChecked) --- + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { 'ts-expect-error': 'allow-with-description' }, + ], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowExpressions: true, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: true, + allowIIFEs: true, + }, + ], + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/strict-boolean-expressions': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/promise-function-async': 'error', + '@typescript-eslint/return-await': ['error', 'always'], + '@typescript-eslint/require-array-sort-compare': 'error', + '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/method-signature-style': 'error', + + // --- module hygiene --- + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/consistent-type-exports': 'error', + '@typescript-eslint/no-import-type-side-effects': 'error', + '@typescript-eslint/consistent-type-definitions': ['error', 'type'], + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['../*'], + message: + 'Use the @/ path alias instead of relative parent paths.', + }, + ], + }, + ], + + // --- naming & scoping --- + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'default', + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + }, + { selector: 'function', format: ['camelCase', 'PascalCase'] }, + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'enumMember', format: ['PascalCase', 'UPPER_CASE'] }, + // Konva/DOM/third-party config keys are dictated by their APIs. + { selector: ['objectLiteralProperty', 'typeProperty'], format: null }, + { selector: 'import', format: null }, + ], + + // --- base rules (mirrors mindee-api-nodejs where not covered by Prettier) --- + eqeqeq: 'error', + 'no-eval': 'error', + 'no-else-return': 'error', + 'no-nested-ternary': 'error', + 'no-implicit-coercion': 'error', + 'no-param-reassign': 'error', + 'default-case-last': 'error', + 'prefer-template': 'error', + 'object-shorthand': 'error', + 'no-console': ['error', { allow: ['warn', 'error'] }], + }, + }, + { + files: ['**/*.spec.{ts,tsx}', 'cypress/**/*.ts'], + languageOptions: { + globals: { ...globals.mocha, cy: 'readonly', Cypress: 'readonly' }, + }, + rules: { + '@typescript-eslint/no-deprecated': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, + }, + { + // Declaration merging (e.g. augmenting Cypress.Chainable) requires interfaces. + files: ['**/*.d.ts'], + rules: { '@typescript-eslint/consistent-type-definitions': 'off' }, + }, + { + files: ['*.config.ts'], + languageOptions: { globals: globals.node }, + }, + prettier, + { + rules: { curly: ['error', 'all'] }, + }, +); diff --git a/package.json b/package.json index a737a14..f471cf4 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,15 @@ "type": "git", "url": "git+https://github.com/mindee/react-mindee-js.git" }, - "main": "dist/index.mjs", - "module": "dist/index.mjs", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", "types": "dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "default": "./dist/index.mjs" + "import": "./dist/index.js", + "default": "./dist/index.js" }, "./package.json": "./package.json" }, @@ -31,40 +32,35 @@ "packageManager": "pnpm@12.8.1", "dependencies": { "heic2any": "^0.0.4", - "konva": "^8.1.3", - "pdfjs-dist": "3.11.174", - "utif": "^3.1.0", - "uuid": "^8.3.2" + "konva": "^10.7.0", + "pdfjs-dist": "^6.3.289", + "utif": "^3.1.0" }, "devDependencies": { - "@cypress/react": "^7.0.3", - "@cypress/vite-dev-server": "^2.0.7", - "@ianvs/prettier-plugin-sort-imports": "^4.0.2", - "@types/cypress-image-snapshot": "^3.1.6", - "@types/jest": "^27.0.1", - "@types/node": "^20.3.3", + "@eslint/js": "^10.0.1", + "@ianvs/prettier-plugin-sort-imports": "^4.7.1", + "@j178/prek": "^0.5.4", + "@microsoft/api-extractor": "^7.59.3", + "@simonsmith/cypress-image-snapshot": "^11.0.0", + "@types/node": "^24.19.0", "@types/react": "^19.2.9", "@types/react-dom": "^19.2.3", - "@types/utif": "^3.0.1", - "@types/uuid": "^8.3.1", - "@typescript-eslint/eslint-plugin": "^5.61.0", - "@typescript-eslint/parser": "^5.61.0", - "@vitejs/plugin-react": "^4.0.1", - "cypress": "^12.16.0", - "cypress-image-snapshot": "^4.0.1", - "eslint": "^8.44.0", - "eslint-config-prettier": "^6.15.0", - "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-unused-imports": "^2.0.0", - "prettier": "^2.8.8", + "@types/utif": "^3.0.6", + "@vitejs/plugin-react": "^6.1.1", + "cypress": "^16.1.0", + "eslint": "^10.11.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react-hooks": "^7.1.1", + "globals": "^17.12.0", + "jiti": "^2.7.0", + "license-checker-rseidelsohn": "^5.0.1", + "prettier": "^3.9.9", "react": "^19.2.3", "react-dom": "^19.2.3", - "ts-patch": "^1.4.3", - "typescript": "^4.9.5", - "typescript-transform-paths": "^3.3.1", - "vite": "^4.3.9", - "vite-plugin-dts": "^3.0.3", - "vite-tsconfig-paths": "^4.2.0" + "typescript": "^6.0.3", + "typescript-eslint": "^8.71.0", + "vite": "^8.3.1", + "vite-plugin-dts": "^5.1.1" }, "peerDependencies": { "react": "^18 || ^19", @@ -72,21 +68,24 @@ }, "scripts": { "preinstall": "npx only-allow pnpm", + "prepare": "prek install --hook-type pre-commit --hook-type pre-push", "dev": "vite build --watch", "cy:prepare": "pnpm build", - "prepare": "ts-patch install -s", "build": "vite build", "cy:open": "cypress open --component --browser chrome", - "cy:run": "cypress run --component --browser chrome", + "cy:run": "cypress run --component --browser chrome --spec 'src/**/*.spec.{ts,tsx}'", + "cy:run:integration": "cypress run --component --browser chrome --spec 'cypress/integration/**/*.spec.{ts,tsx}'", "docs:start": "pnpm --dir ./docs start", "docs:install": "pnpm --dir ./docs install", "docs:prepare": "pnpm build && pnpm docs:install", "docs:update": "pnpm --dir ./docs add ../", "docs:build": "pnpm --dir ./docs build", "docs:deploy": "pnpm --dir ./docs deploy", - "prettier:check": "prettier --check ./src", - "prettier:write": "prettier --write ./src", - "lint": "tsc --noemit && eslint --fix --ext .ts,.tsx . && prettier --write ./src" + "prettier:check": "prettier --check .", + "prettier:write": "prettier --write .", + "lint": "tsc --noEmit && eslint --max-warnings 0 . && prettier --check .", + "lint:fix": "eslint --fix . && prettier --write .", + "license:check": "license-checker-rseidelsohn --production=false --onlyAllow \"MIT;ISC;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;BlueOak-1.0.0;CC0-1.0;CC-BY-3.0;CC-BY-4.0;MPL-2.0;Unlicense;Zlib;AFL-2.1\" --summary" }, "keywords": [ "react", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a43e9b6..d12fdc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,17 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + fast-uri@>=3.0.0 <3.1.9: ^3.1.9 + ip-address@<=10.7.3: ^10.7.4 + pdfjs-dist@<=4.1.392: ^4.2.67 + source-map-js@>=1.0.0 <1.2.2: ^1.2.2 + tar@<=7.5.20: ^7.5.21 + undici@>=6.5.0 <6.29.1: ^6.29.1 + uuid@<11.1.1: ^11.1.1 + vite@>=8.3.0 <=8.3.2: ^8.3.3 + zod@<4.6.6: ^4.6.6 + importers: .: @@ -170,36 +181,33 @@ importers: specifier: ^0.0.4 version: 0.0.4 konva: - specifier: ^8.1.3 - version: 8.4.3 + specifier: ^10.7.0 + version: 10.7.0 pdfjs-dist: - specifier: 3.11.174 - version: 3.11.174(supports-color@8.1.1) + specifier: ^6.3.289 + version: 6.3.289 utif: specifier: ^3.1.0 version: 3.1.0 - uuid: - specifier: ^8.3.2 - version: 8.3.2 devDependencies: - '@cypress/react': - specifier: ^7.0.3 - version: 7.0.3(@types/react@19.3.0)(cypress@12.17.4)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@cypress/vite-dev-server': - specifier: ^2.0.7 - version: 2.2.3(supports-color@8.1.1)(vite@4.5.14(@types/node@20.19.43)) + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1)) '@ianvs/prettier-plugin-sort-imports': - specifier: ^4.0.2 - version: 4.7.1(prettier@2.8.8)(supports-color@8.1.1) - '@types/cypress-image-snapshot': - specifier: ^3.1.6 - version: 3.1.9 - '@types/jest': - specifier: ^27.0.1 - version: 27.5.2 + specifier: ^4.7.1 + version: 4.7.1(prettier@3.9.9)(supports-color@8.1.1) + '@j178/prek': + specifier: ^0.5.4 + version: 0.5.4 + '@microsoft/api-extractor': + specifier: ^7.59.3 + version: 7.59.3(@types/node@24.19.0) + '@simonsmith/cypress-image-snapshot': + specifier: ^11.0.0 + version: 11.0.0(cypress@16.1.0) '@types/node': - specifier: ^20.3.3 - version: 20.19.43 + specifier: ^24.19.0 + version: 24.19.0 '@types/react': specifier: ^19.2.9 version: 19.3.0 @@ -207,65 +215,53 @@ importers: specifier: ^19.2.3 version: 19.3.0(@types/react@19.3.0) '@types/utif': - specifier: ^3.0.1 + specifier: ^3.0.6 version: 3.0.6 - '@types/uuid': - specifier: ^8.3.1 - version: 8.3.4 - '@typescript-eslint/eslint-plugin': - specifier: ^5.61.0 - version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) - '@typescript-eslint/parser': - specifier: ^5.61.0 - version: 5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) '@vitejs/plugin-react': - specifier: ^4.0.1 - version: 4.7.0(supports-color@8.1.1)(vite@4.5.14(@types/node@20.19.43)) + specifier: ^6.1.1 + version: 6.1.1(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)) cypress: - specifier: ^12.16.0 - version: 12.17.4 - cypress-image-snapshot: - specifier: ^4.0.1 - version: 4.0.1(cypress@12.17.4)(jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1)) + specifier: ^16.1.0 + version: 16.1.0 eslint: - specifier: ^8.44.0 - version: 8.57.1(supports-color@8.1.1) + specifier: ^10.11.0 + version: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) eslint-config-prettier: - specifier: ^6.15.0 - version: 6.15.0(eslint@8.57.1(supports-color@8.1.1)) - eslint-plugin-prettier: - specifier: ^3.1.4 - version: 3.4.1(eslint-config-prettier@6.15.0(eslint@8.57.1(supports-color@8.1.1)))(eslint@8.57.1(supports-color@8.1.1))(prettier@2.8.8) - eslint-plugin-unused-imports: - specifier: ^2.0.0 - version: 2.0.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1)) + specifier: ^10.1.8 + version: 10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1)) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1) + globals: + specifier: ^17.12.0 + version: 17.12.0 + jiti: + specifier: ^2.7.0 + version: 2.7.0 + license-checker-rseidelsohn: + specifier: ^5.0.1 + version: 5.0.1(supports-color@8.1.1) prettier: - specifier: ^2.8.8 - version: 2.8.8 + specifier: ^3.9.9 + version: 3.9.9 react: specifier: ^19.2.3 version: 19.3.0 react-dom: specifier: ^19.2.3 version: 19.3.0(react@19.3.0) - ts-patch: - specifier: ^1.4.3 - version: 1.4.5(typescript@4.9.5) typescript: - specifier: ^4.9.5 - version: 4.9.5 - typescript-transform-paths: - specifier: ^3.3.1 - version: 3.5.6(typescript@4.9.5) + specifier: ^6.0.3 + version: 6.0.3 + typescript-eslint: + specifier: ^8.71.0 + version: 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) vite: - specifier: ^4.3.9 - version: 4.5.14(@types/node@20.19.43) + specifier: ^8.3.3 + version: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) vite-plugin-dts: - specifier: ^3.0.3 - version: 3.9.1(@types/node@20.19.43)(rollup@3.30.0)(supports-color@8.1.1)(typescript@4.9.5)(vite@4.5.14(@types/node@20.19.43)) - vite-tsconfig-paths: - specifier: ^4.2.0 - version: 4.3.2(supports-color@8.1.1)(typescript@4.9.5)(vite@4.5.14(@types/node@20.19.43)) + specifier: ^5.1.1 + version: 5.1.1(@microsoft/api-extractor@7.59.3(@types/node@24.19.0))(rolldown@1.2.11)(rollup@3.30.0)(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)) packages: @@ -303,10 +299,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.29.7': - resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} @@ -328,97 +320,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.29.7': - resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -431,173 +332,19 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@cnakazawa/watch@1.0.4': - resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} - engines: {node: '>=0.1.95'} - hasBin: true - - '@colors/colors@1.5.0': - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - - '@cypress/react@7.0.3': - resolution: {integrity: sha512-YseqnMugTbdPV9YCYEMXVqIf+P7x+pfjXOdjv4dnDFqNCZeHaZfOZVFZ4XfEHVxMv0aDszxlaLiIp3QDPhr12w==} - peerDependencies: - '@types/react': ^16.9.16 || ^17.0.0 - cypress: '*' - react: ^=16.x || ^=17.x - react-dom: ^=16.x || ^=17.x - peerDependenciesMeta: - '@types/react': - optional: true + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} - '@cypress/request@2.88.12': - resolution: {integrity: sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==} - engines: {node: '>= 6'} + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} - '@cypress/vite-dev-server@2.2.3': - resolution: {integrity: sha512-E9cPKwReweYGRsupfR6Va1R1bHv3zPb3gHG68fyQwAjG4oPORaQlgfFWiR2i1pF+tRftvNfM0O2PBuKX3IvPxg==} - peerDependencies: - vite: '>= 2.1.3' + '@cypress/request@4.0.1': + resolution: {integrity: sha512-y20e+e6dFYkOUUJLVUZTsJRuTiXZaUQ32WD+R/ux/HBybbTx4ge7cNINcua0pU8+SNkKuRbOF12mBmzuzM8n5w==} + engines: {node: '>= 14.17.0'} '@cypress/xvfb@1.2.4': resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -608,26 +355,58 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@eslint/plugin-kit@0.7.3': + resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@gar/promise-retry@1.0.3': + resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@ianvs/prettier-plugin-sort-imports@4.7.1': resolution: {integrity: sha512-jmTNYGlg95tlsoG3JLCcuC4BrFELJtLirLAkQW/71lXSyOhVt/Xj7xWbbGcuVbNq1gwWgSyMrPjJc9Z30hynVw==} @@ -647,57 +426,75 @@ packages: prettier-plugin-ember-template-tag: optional: true - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} - engines: {node: '>=8'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} - '@jest/console@26.6.2': - resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==} - engines: {node: '>= 10.14.2'} + '@isaacs/string-locale-compare@1.1.0': + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} - '@jest/core@26.6.3': - resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==} - engines: {node: '>= 10.14.2'} + '@j178/prek-android-arm64@0.5.4': + resolution: {integrity: sha512-K2kVaCpRmuQjr0sntQEMdTrAP2qTweWWYybyZyDwdIw0AC96Woo4zG7Tho0sJlSJcfcMVFWE34rgfhHB2NaRgA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] - '@jest/environment@26.6.2': - resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==} - engines: {node: '>= 10.14.2'} + '@j178/prek-darwin-arm64@0.5.4': + resolution: {integrity: sha512-/dzoIpm/Zno+ZNRo4X6crvH5xmTZBieU+8yjXPkBFsiGSqZeN3HfoERQFAZ41C2k0OJnHdzOpnHKlBgAvHD5pg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] - '@jest/fake-timers@26.6.2': - resolution: {integrity: sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==} - engines: {node: '>= 10.14.2'} + '@j178/prek-darwin-x64@0.5.4': + resolution: {integrity: sha512-k1j1CSXxG9MJtPW1IvJONoyLVlv2EKCVnJk73tvqnkKd3mkDq+x1cAiSbbEoGAtwMqhd7T9bnj5pGQENAaR/5A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] - '@jest/globals@26.6.2': - resolution: {integrity: sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==} - engines: {node: '>= 10.14.2'} + '@j178/prek-linux-arm64-gnu@0.5.4': + resolution: {integrity: sha512-fgB19MdxpBeVzRya/dBj0zpDhCtvjiCm/BDMPhpu7FhSFbOJ18bTu9jUgKAclfRik1dkXLLeLA/2e+kGrmfBfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@jest/reporters@26.6.2': - resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==} - engines: {node: '>= 10.14.2'} + '@j178/prek-linux-arm64-musl@0.5.4': + resolution: {integrity: sha512-IhaFnZPXlOYqBayCv/75sn7THBDamvi/b+o0IiceQEb9qvcDwmfVwZufC6kDpRWoL2ESXJPjYXoQsl+Lr55ZEw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + libc: [musl] - '@jest/source-map@26.6.2': - resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==} - engines: {node: '>= 10.14.2'} + '@j178/prek-linux-x64-gnu@0.5.4': + resolution: {integrity: sha512-srSCqoDuq/dTs6sg+B193PmRqPsbT4ibY66KE/pkaAWOzWc6dSpECtsJQkymH172ewI4Gs9G5ZR8m7KCtqFiZA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + libc: [glibc] - '@jest/test-result@26.6.2': - resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==} - engines: {node: '>= 10.14.2'} + '@j178/prek-linux-x64-musl@0.5.4': + resolution: {integrity: sha512-nbJod1QxtJls9O+FLpwItrXm0aL8LGJlz0itdDgwvCem0IhSHnOikuyy16R1x/WA5AOVMA0EMbHcbivym+a7Mg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + libc: [musl] - '@jest/test-sequencer@26.6.3': - resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==} - engines: {node: '>= 10.14.2'} + '@j178/prek-win32-arm64-msvc@0.5.4': + resolution: {integrity: sha512-BqBWp6iBhxcPX+o5+v31xh1l6LYMqq/YomNd/LYvyX+QCL1Xptd2FgG5K0Zq2FG0C7y6isANFMifVdnoA3oKsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] - '@jest/transform@26.6.2': - resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} - engines: {node: '>= 10.14.2'} + '@j178/prek-win32-x64-msvc@0.5.4': + resolution: {integrity: sha512-p/1rouJZGcLq+MMHQ9PbfEvXT9r2iav2yB6anlH0FofbSbO5gzJUEsxVsuSF0yHvk5a6jnHiuS0IK5TDXdNKGA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] - '@jest/types@26.6.2': - resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} - engines: {node: '>= 10.14.2'} + '@j178/prek@0.5.4': + resolution: {integrity: sha512-QalDi4SSAJ78p8kJvHi3/hwG63WfuE8oHpj6+SlRWgs3eVpgTOMjawe3bq0C2vPVFvxfwI8YVRx8PCWBfrcc5A==} + engines: {node: '>=18'} + hasBin: true '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -715,388 +512,640 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@mapbox/node-pre-gyp@1.0.11': - resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 - '@microsoft/api-extractor-model@7.28.13': - resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@microsoft/api-extractor@7.43.0': - resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + '@microsoft/api-extractor-model@7.33.14': + resolution: {integrity: sha512-HdFzU14NGrm04SxA8CfEByX4R1S06o08qVCrGgIepb4miUgky771TwiDi2b3/ZJSQ1QKEJzq452MW09zenL19w==} + engines: {node: '>=20.9.0'} + + '@microsoft/api-extractor@7.59.3': + resolution: {integrity: sha512-L1w0pe8/Af3PhjT7JDSsYZAtwFQ3l5pw7QjiNiNFpHA32nvvwYpvHWPedu8ulaJYiNWdKKq7We0FLSfZt0mbFA==} + engines: {node: '>=20.9.0'} hasBin: true - '@microsoft/tsdoc-config@0.16.2': - resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + '@microsoft/tsdoc-config@0.18.2': + resolution: {integrity: sha512-4ymuLTQ9lFwJXK15f41iPcNHflymjk02Ltq5f5wo3fA02O1nHIGSc1TIZwzTSQZep+jXHM644jobt4Q8O3vtqg==} - '@microsoft/tsdoc@0.14.2': - resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + '@microsoft/tsdoc@0.17.0': + resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@napi-rs/canvas-android-arm64@1.0.9': + resolution: {integrity: sha512-4LGXk2/0HVzE29K8SzML5WubgCp++B1FH3qgl35XmSZE+lLdr6P9VRQEnZ0MCLZMTSuJP41yyhtoiVNEuvrTIA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@napi-rs/canvas-darwin-arm64@1.0.9': + resolution: {integrity: sha512-YNdfLBzY0W/Pep9fo2L6RmoNlNksnn05LRnX66W63R3ij58S25QOTcjdtEt2v8+PnCESzqZsYzUo+QPeIR44NA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@napi-rs/canvas-darwin-x64@1.0.9': + resolution: {integrity: sha512-ceZQSknTEcy3dOXoekv59LTCkXjvnLsq+VW5PeNNDHEPQbRS5Ervkm1EaDa7WLAjiYWMLuSQTRTHao1dEX4prg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.9': + resolution: {integrity: sha512-XhfI0Wwv4llhd6nnWDtY3kQKjq0r+y1i91PlJlJI24ag2U9WrnwbG1qS3+fDLEyouwEVFchiKkTEHozK+5iUNA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] - '@rollup/pluginutils@5.4.0': - resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@napi-rs/canvas-linux-arm64-gnu@1.0.9': + resolution: {integrity: sha512-012oiYtKaE7i9oxc8q7nraT7kDOpLcaCmFLzVe9Ty34RHDdoDzbWLrVh827CNxYh/EADX1eSikA3ymLjo/nNuw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@rushstack/node-core-library@4.0.2': - resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true + '@napi-rs/canvas-linux-arm64-musl@1.0.9': + resolution: {integrity: sha512-Ls5UWYFFn63casTZEczbeyEg3vRDRkv9lscuGwfchtY5yLLQhgOB8SN4YGxmfJ5vTaBwZ2YUxBS3NtmjJmFXdA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] - '@rushstack/rig-package@0.5.2': - resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + '@napi-rs/canvas-linux-riscv64-gnu@1.0.9': + resolution: {integrity: sha512-hLKEGxV7ZiRHqndePTokgDMdBlo/rDfzg7P4p4QIv9pUhuYobnu3R2NIFLCRghG0nwfo+s2sw+c1xZFeCmEAsw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] - '@rushstack/terminal@0.10.0': - resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true + '@napi-rs/canvas-linux-x64-gnu@1.0.9': + resolution: {integrity: sha512-6kaz3w0QMy77PDWk6rJ1ksIihdad3qzEyX2o2oGT8GwCaypfT5mhjr8buOO5hstyLxcWXDScuz56RsINLtBPIQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] - '@rushstack/ts-command-line@4.19.1': - resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + '@napi-rs/canvas-linux-x64-musl@1.0.9': + resolution: {integrity: sha512-xrGvmS3v55hmZ86ls/kBLVNMUTYio3f6Ik0DireemG994VfPAwiA3ZXA0Uf1bByctkB3NQ1Sfb+H5bkdUnnzfQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] - '@sinonjs/commons@1.8.6': - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} + '@napi-rs/canvas-win32-arm64-msvc@1.0.9': + resolution: {integrity: sha512-yjmVS3ArZeRVCP7jqbPq4rpZa/BhTeI7ELE2XqJg3snICQBDevLZyArxswHkiTnT34KRic33/4fLirrHI+SY8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] - '@sinonjs/fake-timers@6.0.1': - resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==} + '@napi-rs/canvas-win32-x64-msvc@1.0.9': + resolution: {integrity: sha512-QlSYQdMQslB81nlABo9wNfQ6npFhE7/O+saCZdqVGueGanyRk4jCogD5EwQenfP3kIq9e+mm6GreQBjX5MrA8g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] - '@tootallnate/once@1.1.2': - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} + '@napi-rs/canvas@1.0.9': + resolution: {integrity: sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ==} + engines: {node: '>= 10'} - '@types/argparse@1.0.38': - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@npmcli/agent@4.0.2': + resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@npmcli/arborist@9.6.0': + resolution: {integrity: sha512-Dku9UWbrrX+UCu8rQ1obGKaQAL4kwdt3hHCNXrd0n0R/4B8oq3CzloUAShwFjfsAGM6KY27gPuNftOUEZ4nhOw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + '@npmcli/git@7.0.2': + resolution: {integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@npmcli/installed-package-contents@4.0.0': + resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true - '@types/cypress-image-snapshot@3.1.9': - resolution: {integrity: sha512-vcFgB8AfuM0dtupCHAGgapUbjRNWmMDtpzzpXz5gexWOHW4y/n10XRi5/y5Xpi0Ztku+X4dnRVMqQa5IDcy3Ig==} + '@npmcli/map-workspaces@5.0.3': + resolution: {integrity: sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@npmcli/metavuln-calculator@9.0.3': + resolution: {integrity: sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@npmcli/name-from-folder@4.0.0': + resolution: {integrity: sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + '@npmcli/node-gyp@5.0.0': + resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + '@npmcli/package-json@7.0.5': + resolution: {integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@npmcli/promise-spawn@9.0.1': + resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/jest@27.5.2': - resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} + '@npmcli/query@5.0.0': + resolution: {integrity: sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@npmcli/redact@4.0.0': + resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/node@16.18.126': - resolution: {integrity: sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==} + '@npmcli/run-script@10.0.4': + resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} + engines: {node: ^20.17.0 || >=22.9.0} - '@types/node@20.19.43': - resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + '@oxc-project/types@0.151.0': + resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@oxc-project/types@0.155.0': + resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==} - '@types/prettier@2.7.3': - resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + '@rolldown/binding-android-arm-eabi@1.2.11': + resolution: {integrity: sha512-A5kXfGKvKWWZE0TtPrfsvT+q4Y5d1QG8gGUzpYjGydM+fARM9MuX90PrXYXe0XbsDVgyxxNzHo6giCj90bsFNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@types/react-dom@19.3.0': - resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} - peerDependencies: - '@types/react': ^19.3.0 + '@rolldown/binding-android-arm-eabi@1.2.15': + resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@types/react@19.3.0': - resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + '@rolldown/binding-android-arm64@1.2.11': + resolution: {integrity: sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@types/semver@7.8.0': - resolution: {integrity: sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==} + '@rolldown/binding-android-arm64@1.2.15': + resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@types/sinonjs__fake-timers@8.1.1': - resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} + '@rolldown/binding-darwin-arm64@1.2.11': + resolution: {integrity: sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@types/sizzle@2.3.10': - resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==} + '@rolldown/binding-darwin-arm64@1.2.15': + resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.11': + resolution: {integrity: sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@rolldown/binding-darwin-x64@1.2.15': + resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@types/utif@3.0.6': - resolution: {integrity: sha512-4XHDJkLHoUeLA2P/VOd6dpDJpSTpV84tN9EyHojZrW22jtUR0VrdLwVRUAw+oI3g72vSzOo+YQOzfwcW6WYHLw==} + '@rolldown/binding-freebsd-x64@1.2.11': + resolution: {integrity: sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.2.15': + resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@types/uuid@8.3.4': - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + resolution: {integrity: sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.15': + resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@types/yargs@15.0.20': - resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==} + '@rolldown/binding-linux-arm64-gnu@1.2.11': + resolution: {integrity: sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@rolldown/binding-linux-arm64-gnu@1.2.15': + resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@typescript-eslint/eslint-plugin@5.62.0': - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rolldown/binding-linux-arm64-musl@1.2.11': + resolution: {integrity: sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-arm64-musl@1.2.15': + resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.11': + resolution: {integrity: sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-ppc64-gnu@1.2.15': + resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.11': + resolution: {integrity: sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.15': + resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.11': + resolution: {integrity: sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.15': + resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.11': + resolution: {integrity: sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-musl@1.2.15': + resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.11': + resolution: {integrity: sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.2.15': + resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.11': + resolution: {integrity: sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-arm64-msvc@1.2.15': + resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.11': + resolution: {integrity: sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.15': + resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: - typescript: + rollup: optional: true - '@typescript-eslint/parser@5.62.0': - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rushstack/node-core-library@5.25.0': + resolution: {integrity: sha512-JWd5MVInIQqTlujjqV2gO4aJi/QZvBdGCD5jQ4tF0Swcz1Ba80fP0dvxIYTaHawUujyQaCixtzzz9LM8bM26hQ==} + engines: {node: '>=20.9.0'} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' + '@types/node': '*' peerDependenciesMeta: - typescript: + '@types/node': optional: true - '@typescript-eslint/scope-manager@5.62.0': - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@typescript-eslint/type-utils@5.62.0': - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} peerDependencies: - eslint: '*' - typescript: '*' + '@types/node': '*' peerDependenciesMeta: - typescript: + '@types/node': optional: true - '@typescript-eslint/types@5.62.0': - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rushstack/rig-package@0.7.3': + resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==} - '@typescript-eslint/typescript-estree@5.62.0': - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rushstack/terminal@0.24.6': + resolution: {integrity: sha512-MuSbKmMnP8CVEEBZ2kJpE22farLfxrScOrJ3pN3PoCOSaxemEhD/0H1lXZYj2PmKJxQMHb5pib5rVfIlOX/DlA==} + engines: {node: '>=20.9.0'} peerDependencies: - typescript: '*' + '@types/node': '*' peerDependenciesMeta: - typescript: + '@types/node': optional: true - '@typescript-eslint/utils@5.62.0': - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@rushstack/ts-command-line@5.3.16': + resolution: {integrity: sha512-k1L7MQQEuQ1v0rsQ8pRzoZ3NRsrvSifBguWlLnl2a5iWGTTW9E0/G/B39291eAf3zF45wheybuJ2onwB/PPsfA==} + engines: {node: '>=20.9.0'} + + '@sigstore/bundle@4.0.0': + resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/core@3.2.1': + resolution: {integrity: sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/protobuf-specs@0.5.2': + resolution: {integrity: sha512-SQqvFMt4V78fdjcDdYX6HbiVSOR4QK3ZgwCa2KOsopAgPIHy1rU5UDUmzLl02r5oyyaYcYHR1hpwDRk/yUe+Mw==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/sign@4.1.1': + resolution: {integrity: sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/tuf@4.0.2': + resolution: {integrity: sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/verify@3.1.1': + resolution: {integrity: sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@simonsmith/cypress-image-snapshot@11.0.0': + resolution: {integrity: sha512-fqiH4/0DARt4vR8U4d7mMyrWAdKlirG+0dNksZbMbP6a4zRPo6q7EmOSvRuse8kShbmrg7UogvlApjE9mN+crQ==} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + cypress: '>=15.10.0' - '@typescript-eslint/visitor-keys@5.62.0': - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@tufjs/canonical-json@2.0.0': + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@tufjs/models@4.1.0': + resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.19.0': + resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==} + + '@types/pixelmatch@5.2.6': + resolution: {integrity: sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@types/sinonjs__fake-timers@8.1.1': + resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} - '@ungap/structured-clone@1.4.0': - resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + '@types/sizzle@2.3.10': + resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==} + + '@types/tmp@0.2.6': + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} + + '@types/utif@3.0.6': + resolution: {integrity: sha512-4XHDJkLHoUeLA2P/VOd6dpDJpSTpV84tN9EyHojZrW22jtUR0VrdLwVRUAw+oI3g72vSzOo+YQOzfwcW6WYHLw==} + + '@typescript-eslint/eslint-plugin@8.71.0': + resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.71.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} - engines: {node: ^14.18.0 || >=16.0.0} + '@typescript-eslint/parser@8.71.0': + resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@volar/language-core@1.11.1': - resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + '@typescript-eslint/project-service@8.71.0': + resolution: {integrity: sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' - '@volar/source-map@1.11.1': - resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + '@typescript-eslint/scope-manager@8.71.0': + resolution: {integrity: sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@volar/typescript@1.11.1': - resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + '@typescript-eslint/tsconfig-utils@8.71.0': + resolution: {integrity: sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.71.0': + resolution: {integrity: sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.71.0': + resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.71.0': + resolution: {integrity: sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.71.0': + resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.71.0': + resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: ^0.145.0 + vite: ^8.3.3 peerDependenciesMeta: - typescript: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + oxc-transform-react: optional: true - '@vue/compiler-core@3.5.43': - resolution: {integrity: sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==} + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} - '@vue/compiler-dom@3.5.43': - resolution: {integrity: sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==} + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} - '@vue/language-core@1.8.27': - resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@vue/shared@3.5.43': - resolution: {integrity: sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==} - - '@xmldom/xmldom@0.9.12': - resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} - engines: {node: '>=14.6'} + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - deprecated: Use your platform's native atob() and btoa() methods instead - - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - - acorn-globals@6.0.0: - resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + abbrev@4.0.0: + resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} + engines: {node: ^20.17.0 || >=22.9.0} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@7.2.0: - resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} - engines: {node: '>=0.4.0'} - - acorn@7.4.1: - resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.18.0: resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - ansi-regex@2.1.1: - resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} - engines: {node: '>=0.10.0'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-styles@2.2.1: - resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} - engines: {node: '>=0.10.0'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-regex@6.4.0: + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} + engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - anymatch@2.0.0: - resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - app-path@3.3.0: - resolution: {integrity: sha512-EAgEXkdcxH1cgEePOSsmUtw9ItPl0KTxnh/pj9ZbhvbKbij9x0oX6PWpGnorDr0DS5AosLgoa5n3T/hZmKQpYA==} - engines: {node: '>=8'} - - aproba@2.1.0: - resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} - - arch@2.2.0: - resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} - are-we-there-yet@2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - deprecated: This package is no longer supported. + arch@3.0.0: + resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - arr-diff@4.0.0: - resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} - engines: {node: '>=0.10.0'} - - arr-flatten@1.1.0: - resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} - engines: {node: '>=0.10.0'} - - arr-union@3.1.0: - resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} - engines: {node: '>=0.10.0'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array-unique@0.3.2: - resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + array-find-index@1.0.2: + resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} asn1@0.2.6: @@ -1106,17 +1155,6 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - assign-symbols@1.0.0: - resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} - engines: {node: '>=0.10.0'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -1124,52 +1162,19 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - atob@2.1.2: - resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} - engines: {node: '>= 4.5.0'} - hasBin: true - aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - babel-jest@26.6.3: - resolution: {integrity: sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==} - engines: {node: '>= 10.14.2'} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@26.6.2: - resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==} - engines: {node: '>= 10.14.2'} - - babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} - peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 - - babel-preset-jest@26.6.2: - resolution: {integrity: sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==} - engines: {node: '>= 10.14.2'} - peerDependencies: - '@babel/core': ^7.0.0 - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - base@0.11.2: - resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} - engines: {node: '>=0.10.0'} - baseline-browser-mapping@2.11.26: resolution: {integrity: sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ==} engines: {node: '>=6.0.0'} @@ -1178,49 +1183,34 @@ packages: bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bin-links@6.0.2: + resolution: {integrity: sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w==} + engines: {node: ^20.17.0 || >=22.9.0} + blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - brace-expansion@1.1.21: - resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - - brace-expansion@2.1.7: - resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} - - braces@2.3.2: - resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} - engines: {node: '>=0.10.0'} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browser-process-hrtime@1.0.0: - resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} browserslist@4.29.2: resolution: {integrity: sha512-/u9r8k8ue4ZhHCw9ovDtltpWdXeXzjhdxCGj6vm1RGLPXb8lu33EMdoCHF5Sx8oYBS+Q/FYYNmAqlHncxR3RmA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - cache-base@1.0.1: - resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} - engines: {node: '>=0.10.0'} + cacache@20.0.4: + resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} + engines: {node: ^20.17.0 || >=22.9.0} + + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} @@ -1234,215 +1224,120 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - caniuse-lite@1.0.30001813: resolution: {integrity: sha512-zfjJo4rM0+fUomGDBW/xcDjhIwz/210DGvip2MAMDZ8KHcRPnOHmEgHPZP0UHlZoxr8fYKVJOqcORhYQcG4FKQ==} - canvas@2.11.2: - resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} - engines: {node: '>=6'} - - capture-exit@2.0.0: - resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} - engines: {node: 6.* || 8.* || >= 10.*} - caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - chalk@1.1.3: - resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} - engines: {node: '>=0.10.0'} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - check-more-types@2.24.0: - resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} - engines: {node: '>= 0.8.0'} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} - ci-info@2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + chrome-remote-interface@0.33.3: + resolution: {integrity: sha512-zNnn0prUL86Teru6UCAZ1yU1XeXljHl3gj7OrfPcarEfU62OUU4IujDPdTDW3dAWwRqN3ZMG/Chhkh2gPL/wiw==} + hasBin: true - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} - cjs-module-lexer@0.6.0: - resolution: {integrity: sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==} - - class-utils@0.3.6: - resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} - engines: {node: '>=0.10.0'} - - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + cli-table3@0.6.1: + resolution: {integrity: sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==} engines: {node: 10.* || >= 12.*} - cli-truncate@2.1.0: - resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} - engines: {node: '>=8'} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} - collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} - - collection-visit@1.0.0: - resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} - engines: {node: '>=0.10.0'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + cmd-shim@8.0.0: + resolution: {integrity: sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==} + engines: {node: ^20.17.0 || >=22.9.0} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@2.11.0: + resolution: {integrity: sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==} + commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} - commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - - computeds@0.0.1: - resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + confbox@0.3.1: + resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + content-type@2.1.0: + resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} + engines: {node: '>=18'} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - copy-descriptor@0.1.1: - resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} - engines: {node: '>=0.10.0'} - core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - cross-spawn@6.0.6: - resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} - engines: {node: '>=4.8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - cssom@0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - - cssom@0.4.4: - resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} - - cssstyle@2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} - engines: {node: '>=8'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - cypress-image-snapshot@4.0.1: - resolution: {integrity: sha512-PBpnhX/XItlx3/DAk5ozsXQHUi72exybBNH5Mpqj1DVmjq+S5Jd9WE5CRa4q5q0zuMZb2V2VpXHth6MjFpgj9Q==} - engines: {node: '>=8'} - peerDependencies: - cypress: ^4.5.0 - - cypress@12.17.4: - resolution: {integrity: sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==} - engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} + cypress@16.1.0: + resolution: {integrity: sha512-t8suSUVzQyHI7v6KhhA07M9ChftdfqVZNLCekhguIb+2f0fCmI8WAih5L0oGH7K6slapNduGy5zpJ+17LmSXkQ==} + engines: {node: ^22.0.0 || ^24.0.0 || >=26.0.0} hasBin: true dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} - data-urls@2.0.0: - resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} - engines: {node: '>=10'} - dayjs@1.11.23: resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -1460,75 +1355,20 @@ packages: supports-color: optional: true - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - - decode-uri-component@0.2.2: - resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} - engines: {node: '>=0.10'} - - decompress-response@4.2.1: - resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} - engines: {node: '>=8'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - define-property@0.2.5: - resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} - engines: {node: '>=0.10.0'} - - define-property@1.0.0: - resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} - engines: {node: '>=0.10.0'} - - define-property@2.0.2: - resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} - engines: {node: '>=0.10.0'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - diff-sequences@26.6.2: - resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==} - engines: {node: '>= 10.14.2'} - - diff-sequences@27.5.1: - resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - domexception@2.0.1: - resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==} - engines: {node: '>=8'} - deprecated: Use your platform's native DOMException instead + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} @@ -1540,9 +1380,8 @@ packages: electron-to-chromium@1.5.440: resolution: {integrity: sha512-SghDzqdJokdz8zP8YNlvS74+CwLRoUPDGvP/gFA+HrKVw+pOshUAgx8pXR37xl/u16zGae746rFNWvZ/22kU2Q==} - emittery@0.7.2: - resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==} - engines: {node: '>=10'} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1550,16 +1389,13 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} @@ -1577,89 +1413,51 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - eslint-config-prettier@6.15.0: - resolution: {integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: - eslint: '>=3.14.1' - - eslint-plugin-prettier@3.4.1: - resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==} - engines: {node: '>=6.0.0'} - peerDependencies: - eslint: '>=5.0.0' - eslint-config-prettier: '*' - prettier: '>=1.13.0' - peerDependenciesMeta: - eslint-config-prettier: - optional: true + eslint: '>=7.0.0' - eslint-plugin-unused-imports@2.0.0: - resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 - eslint: ^8.0.0 - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - - eslint-rule-composer@0.3.0: - resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} - engines: {node: '>=4.0.0'} - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} + eslint@10.11.0: + resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} @@ -1669,10 +1467,6 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -1687,12 +1481,8 @@ packages: eventemitter2@6.4.7: resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==} - exec-sh@0.3.6: - resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==} - - execa@1.0.0: - resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} - engines: {node: '>=6'} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} execa@4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} @@ -1702,134 +1492,70 @@ packages: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} engines: {node: '>=4'} - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - expand-brackets@2.1.4: - resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} - engines: {node: '>=0.10.0'} - - expect@26.6.2: - resolution: {integrity: sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==} - engines: {node: '>= 10.14.2'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - - extend-shallow@3.0.2: - resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} - engines: {node: '>=0.10.0'} + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extglob@2.0.4: - resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} - engines: {node: '>=0.10.0'} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.20.3: - resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} - - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - fill-range@4.0.0: - resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} - engines: {node: '>=0.10.0'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fast-uri@3.1.9: + resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} - find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - for-in@1.0.2: - resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} - engines: {node: '>=0.10.0'} - forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - - form-data@3.0.5: - resolution: {integrity: sha512-j23EibVLnp4zNXGW7LjryXYa2X6U/M96yoOX+ybZxwkYajdxRNEqYY3zhh7y0i6kfISKS2jr+EJq1YTUDEv5+w==} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} - fragment-cache@0.2.1: - resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} - engines: {node: '>=0.10.0'} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@11.3.6: + resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} + engines: {node: '>=14.14'} fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -1839,31 +1565,18 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gauge@3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - deprecated: This package is no longer supported. - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.7.0: + resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==} + engines: {node: '>=18'} get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -1872,58 +1585,31 @@ packages: resolution: {integrity: sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==} engines: {node: '>=0.12.0'} - get-stdin@6.0.0: - resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==} - engines: {node: '>=4'} - - get-stream@4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} - engines: {node: '>=6'} - get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} - get-value@2.0.6: - resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} - engines: {node: '>=0.10.0'} - - getos@3.2.1: - resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} - getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} - global-prefix@3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + globals@17.12.0: + resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} + engines: {node: '>=18'} glur@1.1.2: resolution: {integrity: sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==} @@ -1935,20 +1621,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - growly@1.3.0: - resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==} - - has-ansi@2.0.0: - resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} - engines: {node: '>=0.10.0'} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1961,159 +1633,99 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - - has-value@0.3.1: - resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} - engines: {node: '>=0.10.0'} - - has-value@1.0.0: - resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} - engines: {node: '>=0.10.0'} - - has-values@0.1.4: - resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} - engines: {node: '>=0.10.0'} + hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} - has-values@1.0.0: - resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} - engines: {node: '>=0.10.0'} + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - heic2any@0.0.4: resolution: {integrity: sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==} - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - html-encoding-sniffer@2.0.1: - resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} - engines: {node: '>=10'} + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} - http-proxy-agent@4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} - http-signature@1.3.6: - resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} + engines: {node: ^20.17.0 || >=22.9.0} + + http-cache-semantics@4.3.0: + resolution: {integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-signature@1.4.0: + resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore-walk@8.0.0: + resolution: {integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==} + engines: {node: ^20.17.0 || >=22.9.0} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} + ignore@7.0.10: + resolution: {integrity: sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==} + engines: {node: '>= 4'} import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - inflight@1.0.6: - resolution: {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. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - - is-accessor-descriptor@1.0.2: - resolution: {integrity: sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - - is-ci@2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true + ip-address@10.7.5: + resolution: {integrity: sha512-t2v0rkILYv9WXmSL7TpAaU3HXgaFYOPUcH3QHgwQ++gpJuH3bqyMy4Fvt/DJCTKwzH3Cf+bAp5W/mFL39KPLzA==} + engines: {node: '>= 12'} is-core-module@2.17.0: resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} engines: {node: '>= 0.4'} - is-data-descriptor@1.0.1: - resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} - engines: {node: '>= 0.4'} - - is-descriptor@0.1.8: - resolution: {integrity: sha512-SceYGWXvdqlWa/OnQ5FQuV+NxvNmMRhMw/w9AHkH71hTzveND4BTYgvp16g+oITK47qbOl/3D0bl0iygehWAWQ==} - engines: {node: '>= 0.4'} - - is-descriptor@1.0.4: - resolution: {integrity: sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==} - engines: {node: '>= 0.4'} - - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extendable@1.0.1: - resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} - engines: {node: '>=0.10.0'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2122,9 +1734,9 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -2134,29 +1746,10 @@ packages: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} - is-number@3.0.0: - resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} - engines: {node: '>=0.10.0'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - - is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2168,200 +1761,27 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isobject@2.1.0: - resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} - engines: {node: '>=0.10.0'} - - isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@4.0.3: - resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} - engines: {node: '>=8'} - - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - iterm2-version@4.2.0: - resolution: {integrity: sha512-IoiNVk4SMPu6uTcK+1nA5QaHNok2BMDLjSl5UomrOixe5g4GkylhPwuiGdw00ysSCrXAKNMfFTu+u/Lk5f6OLQ==} - engines: {node: '>=8'} - - jest-changed-files@26.6.2: - resolution: {integrity: sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==} - engines: {node: '>= 10.14.2'} - - jest-cli@26.6.3: - resolution: {integrity: sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==} - engines: {node: '>= 10.14.2'} - hasBin: true - - jest-config@26.6.3: - resolution: {integrity: sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==} - engines: {node: '>= 10.14.2'} - peerDependencies: - ts-node: '>=9.0.0' - peerDependenciesMeta: - ts-node: - optional: true - - jest-diff@26.6.2: - resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==} - engines: {node: '>= 10.14.2'} - - jest-diff@27.5.1: - resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - jest-docblock@26.0.0: - resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==} - engines: {node: '>= 10.14.2'} - - jest-each@26.6.2: - resolution: {integrity: sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==} - engines: {node: '>= 10.14.2'} - - jest-environment-jsdom@26.6.2: - resolution: {integrity: sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==} - engines: {node: '>= 10.14.2'} - - jest-environment-node@26.6.2: - resolution: {integrity: sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==} - engines: {node: '>= 10.14.2'} - - jest-get-type@26.3.0: - resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==} - engines: {node: '>= 10.14.2'} - - jest-get-type@27.5.1: - resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - jest-haste-map@26.6.2: - resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==} - engines: {node: '>= 10.14.2'} - - jest-image-snapshot@4.2.0: - resolution: {integrity: sha512-6aAqv2wtfOgxiJeBayBCqHo1zX+A12SUNNzo7rIxiXh6W6xYVu8QyHWkada8HeRi+QUTHddp0O0Xa6kmQr+xbQ==} - engines: {node: '>= 10.14.2'} - peerDependencies: - jest: '>=20 <=26' - - jest-jasmine2@26.6.3: - resolution: {integrity: sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==} - engines: {node: '>= 10.14.2'} - - jest-leak-detector@26.6.2: - resolution: {integrity: sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==} - engines: {node: '>= 10.14.2'} - - jest-matcher-utils@26.6.2: - resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==} - engines: {node: '>= 10.14.2'} - - jest-matcher-utils@27.5.1: - resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - jest-message-util@26.6.2: - resolution: {integrity: sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==} - engines: {node: '>= 10.14.2'} - - jest-mock@26.6.2: - resolution: {integrity: sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==} - engines: {node: '>= 10.14.2'} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} + jest-image-snapshot@6.5.2: + resolution: {integrity: sha512-frenWThr5ddnnokcX5N4gwi41hA5TiUOdhv/JoGcJrOaktHjrk4/7XbiHKW52lgKX+vei6QkRlgM7fkYQ15nPg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: - jest-resolve: '*' + jest: '>=20 <31' peerDependenciesMeta: - jest-resolve: + jest: optional: true - jest-regex-util@26.0.0: - resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==} - engines: {node: '>= 10.14.2'} - - jest-resolve-dependencies@26.6.3: - resolution: {integrity: sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==} - engines: {node: '>= 10.14.2'} - - jest-resolve@26.6.2: - resolution: {integrity: sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==} - engines: {node: '>= 10.14.2'} - - jest-runner@26.6.3: - resolution: {integrity: sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==} - engines: {node: '>= 10.14.2'} - - jest-runtime@26.6.3: - resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} - engines: {node: '>= 10.14.2'} - hasBin: true - - jest-serializer@26.6.2: - resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} - engines: {node: '>= 10.14.2'} - - jest-snapshot@26.6.2: - resolution: {integrity: sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==} - engines: {node: '>= 10.14.2'} - - jest-util@26.6.2: - resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==} - engines: {node: '>= 10.14.2'} - - jest-validate@26.6.2: - resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==} - engines: {node: '>= 10.14.2'} - - jest-watcher@26.6.2: - resolution: {integrity: sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==} - engines: {node: '>= 10.14.2'} - - jest-worker@26.6.2: - resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} - engines: {node: '>= 10.13.0'} - - jest@26.6.3: - resolution: {integrity: sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==} - engines: {node: '>= 10.14.2'} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true jju@1.4.0: @@ -2370,46 +1790,33 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.15.2: - resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} - hasBin: true - - js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} - hasBin: true - jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdom@16.7.0: - resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} - engines: {node: '>=10'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@5.0.0: + resolution: {integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==} + engines: {node: ^20.17.0 || >=22.9.0} json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-nice@1.1.4: + resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} + json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -2418,93 +1825,139 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + jsprim@2.0.2: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@3.2.2: - resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} - engines: {node: '>=0.10.0'} - - kind-of@4.0.0: - resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} - engines: {node: '>=0.10.0'} + just-diff-apply@5.5.0: + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} + just-diff@6.0.2: + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - konva@8.4.3: - resolution: {integrity: sha512-ARqdgAbdNIougRlOKvkQwHlGhXPRBV4KvhCP+qoPpGoVQwwiJe4Hkdu4HHdRPb9rGUp04jDTAxBzEwBsE272pg==} - - lazy-ass@1.6.0: - resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} - engines: {node: '> 0.8'} - - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} + konva@10.7.0: + resolution: {integrity: sha512-2CwuytBrOlTh7WHCSeXyUeysB2rpwHruUUDD5etYtrzijkCVH+00+CQz5fJUGI0hm7mzxxDLYLw4Wpo+8QGJ4g==} + peerDependencies: + canvas: ^3.0.0 + skia-canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + skia-canvas: + optional: true levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + license-checker-rseidelsohn@5.0.1: + resolution: {integrity: sha512-9X+ikKxt9Hy3zOrOZzW1dXL4St5akoYjLt63Am9JZVzU6aTdN+xfDvqySpnJT+gF/h5RmtMk2waW6TDNNCKbqQ==} + engines: {node: '>=24', npm: '>=11'} + hasBin: true + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] - listr2@3.14.0: - resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} - engines: {node: '>=10.0.0'} - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] - locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. - - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -2512,38 +1965,23 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - log-update@4.0.0: - resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} - engines: {node: '>=10'} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - - map-cache@0.2.2: - resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} - engines: {node: '>=0.10.0'} - - map-visit@1.0.0: - resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} - engines: {node: '>=0.10.0'} + make-fetch-happen@15.0.6: + resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==} + engines: {node: ^20.17.0 || >=22.9.0} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -2552,18 +1990,6 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@3.1.10: - resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} - engines: {node: '>=0.10.0'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2576,148 +2002,131 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - mimic-response@2.1.0: - resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} - engines: {node: '>=8'} - - minimatch@3.0.8: - resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@5.0.2: + resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} engines: {node: '>=8'} - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + minipass-sized@2.0.0: + resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} engines: {node: '>=8'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} - mixin-deep@1.3.2: - resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} - engines: {node: '>=0.10.0'} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} - - nan@2.29.0: - resolution: {integrity: sha512-GlGk3HIvitbvs+LT3g6XUP1kpirKNvmDFwF/bmo6XNWSb/eYEs/O4bfgIEIXCZ+lIOTS5xNwDvSGMw6FJdAhtA==} - - nanoid@3.3.19: - resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanomatch@1.2.13: - resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} - engines: {node: '>=0.10.0'} - - natural-compare-lite@1.4.1: - resolution: {integrity: sha512-8Mz06azhPqiDlxhUaUTjBhNAbib/1MoPr89kgmuV6Ir8ICT2/Q/XSYg6VfL3fq2x8MpSQX8sqyI/NeZ5GsGyXA==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + negotiator@1.1.0: + resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} + engines: {node: '>=18'} - node-notifier@8.0.2: - resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==} + node-gyp@12.4.0: + resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true node-releases@2.0.57: resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} engines: {node: '>=18'} - nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true - normalize-path@2.1.1: - resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} - engines: {node: '>=0.10.0'} + npm-bundled@5.0.0: + resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} + engines: {node: ^20.17.0 || >=22.9.0} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + npm-install-checks@8.0.0: + resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} + engines: {node: ^20.17.0 || >=22.9.0} - npm-run-path@2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} + npm-normalize-package-bin@5.0.0: + resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} + engines: {node: ^20.17.0 || >=22.9.0} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + npm-package-arg@13.0.2: + resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} + engines: {node: ^20.17.0 || >=22.9.0} - npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - deprecated: This package is no longer supported. + npm-packlist@10.0.4: + resolution: {integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==} + engines: {node: ^20.17.0 || >=22.9.0} - nwsapi@2.2.28: - resolution: {integrity: sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==} + npm-pick-manifest@11.0.3: + resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} + engines: {node: ^20.17.0 || >=22.9.0} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + npm-registry-fetch@19.1.1: + resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} + engines: {node: ^20.17.0 || >=22.9.0} - object-copy@0.1.0: - resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} - engines: {node: '>=0.10.0'} + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-visit@1.0.1: - resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} - engines: {node: '>=0.10.0'} - - object.pick@1.3.0: - resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} - engines: {node: '>=0.10.0'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2725,6 +2134,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2732,79 +2145,37 @@ packages: ospath@1.2.2: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} - p-each-series@2.2.0: - resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==} - engines: {node: '>=8'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + p-map@7.0.8: + resolution: {integrity: sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw==} + engines: {node: '>=18'} - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} + pacote@21.5.1: + resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - - pascalcase@0.1.1: - resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} - engines: {node: '>=0.10.0'} + parse-conflict-json@5.0.1: + resolution: {integrity: sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==} + engines: {node: ^20.17.0 || >=22.9.0} path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2812,17 +2183,16 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} - path2d-polyfill@2.0.1: - resolution: {integrity: sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==} - engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdfjs-dist@3.11.174: - resolution: {integrity: sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==} - engines: {node: '>=18'} + pdfjs-dist@6.3.289: + resolution: {integrity: sha512-ZHjSVpDa3D6izMq8/04lvkhkATUmL9px6ChPaXc1k6nU2Mrhlg1/7F0bdUqCwUjw3NsPTfPZsMDUU6ZIcRaeQw==} + engines: {node: '>=22.13.0 || >=24'} pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -2833,10 +2203,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} @@ -2845,25 +2211,15 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - pixelmatch@5.3.0: resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==} hasBin: true - pkg-dir@3.0.0: - resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} - engines: {node: '>=6'} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - plist@3.1.1: - resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==} - engines: {node: '>=10.4.0'} + pkg-types@2.3.3: + resolution: {integrity: sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==} pngjs@3.4.0: resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} @@ -2873,53 +2229,48 @@ packages: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.0'} - posix-character-classes@0.1.1: - resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} - engines: {node: '>=0.10.0'} + postcss-selector-parser@7.1.6: + resolution: {integrity: sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==} + engines: {node: '>=4'} - postcss@8.5.28: - resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + postcss@8.5.31: + resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} + prettier@3.9.9: + resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==} + engines: {node: '>=14'} hasBin: true pretty-bytes@5.6.0: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} - pretty-format@26.6.2: - resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} - engines: {node: '>= 10'} - - pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} + engines: {node: ^20.17.0 || >=22.9.0} process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} + proggy@4.0.0: + resolution: {integrity: sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + promise-all-reject-late@1.0.1: + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} + + promise-call-limit@3.0.2: + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==} proxy-from-env@1.0.0: resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -2927,123 +2278,57 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.10.7: - resolution: {integrity: sha512-SU8Tw69GDcmdCwqC8OksqrQ6w/TGMsKRWGOj5rOaFt1xVwLbReX87/icjHrGDVuS3yfZUHKo2Q26IoAVucBS3Q==} - engines: {node: '>=0.6'} + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + engines: {node: '>=0.6'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} react-dom@19.3.0: resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} peerDependencies: react: ^19.3.0 - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} - engines: {node: '>=0.10.0'} - react@19.3.0: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + read-cmd-shim@6.0.0: + resolution: {integrity: sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==} + engines: {node: ^20.17.0 || >=22.9.0} - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} + request-progress@3.0.0: + resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} - regex-not@1.0.2: - resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - remove-trailing-separator@1.1.0: - resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true - repeat-element@1.1.4: - resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} - engines: {node: '>=0.10.0'} - - repeat-string@1.6.1: - resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} - engines: {node: '>=0.10'} - - request-progress@3.0.0: - resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve-url@0.2.1: - resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} - deprecated: https://github.com/lydell/resolve-url#deprecated - - resolve@1.19.0: - resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} - - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} - engines: {node: '>= 0.4'} - hasBin: true - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported + rolldown@1.2.11: + resolution: {integrity: sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported + rolldown@1.2.15: + resolution: {integrity: sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rollup@3.30.0: @@ -3051,48 +2336,21 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rsvp@4.8.5: - resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==} - engines: {node: 6.* || >= 7.*} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex@1.1.0: - resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sane@4.1.0: - resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} - engines: {node: 6.* || 8.* || >= 10.*} - deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added - hasBin: true - - saxes@5.0.1: - resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} - engines: {node: '>=10'} - scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true @@ -3101,37 +2359,14 @@ packages: engines: {node: '>=10'} hasBin: true - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-value@2.0.1: - resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} - engines: {node: '>=0.10.0'} - - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - - shellwords@0.1.1: - resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==} - side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -3151,65 +2386,44 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} - simple-get@3.1.1: - resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + sigstore@4.1.1: + resolution: {integrity: sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==} + engines: {node: ^20.17.0 || >=22.9.0} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slice-ansi@3.0.0: - resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - - snapdragon-node@2.1.1: - resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} - engines: {node: '>=0.10.0'} - - snapdragon-util@3.0.1: - resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} - engines: {node: '>=0.10.0'} - - snapdragon@0.8.2: - resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} - engines: {node: '>=0.10.0'} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} - source-map-resolve@0.5.3: - resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} - deprecated: See https://github.com/lydell/source-map-resolve#deprecated + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} - source-map-url@0.4.1: - resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} - deprecated: See https://github.com/lydell/source-map-url#deprecated + socks@2.8.10: + resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} + spdx-compare@1.0.0: + resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==} spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -3220,12 +2434,17 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + spdx-license-ids@3.0.24: resolution: {integrity: sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==} - split-string@3.1.0: - resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} - engines: {node: '>=0.10.0'} + spdx-ranges@2.1.1: + resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==} + + spdx-satisfies@6.0.0: + resolution: {integrity: sha512-oOWQocnRbFVtBnBITfFgzjhnOklHossTvI+6C1hB2slvp3HgTsfru5wuo8HY2rQpwSm5JuIhNzIuqOfR5IuojQ==} sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -3238,61 +2457,38 @@ packages: ssim.js@3.5.0: resolution: {integrity: sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - - static-extend@0.1.2: - resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} - engines: {node: '>=0.10.0'} + ssri@13.0.1: + resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} + engines: {node: ^20.17.0 || >=22.9.0} string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} - strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} + string-width@8.3.0: + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + engines: {node: '>=20'} strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-eof@1.0.0: - resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} - engines: {node: '>=0.10.0'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - supports-color@2.0.0: - resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} - engines: {node: '>=0.8.0'} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3301,108 +2497,63 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - supports-hyperlinks@2.3.0: - resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} - engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - term-img@4.1.0: - resolution: {integrity: sha512-DFpBhaF5j+2f7kheKFc1ajsAUUDGOaNPpKPtiIMxlbfud6mvfFZuWGnTRpaujUa5J7yl6cIw/h6nyr4mSsENPg==} - engines: {node: '>=8'} - - terminal-link@2.1.1: - resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} - engines: {node: '>=8'} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + systeminformation@5.33.14: + resolution: {integrity: sha512-9liS0hutibZOitbibm90AK3WZe/1rGhNGZSw+/4TbY5moax6KkWwv1TKJmk9uSOD4bvPPg57VAuuNN+4edIpBg==} + engines: {node: '>=10.0.0'} + os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] + hasBin: true - throat@5.0.0: - resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} + engines: {node: '>=18'} throttleit@1.0.1: resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true tmp@0.2.7: resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - - to-object-path@0.3.0: - resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} - engines: {node: '>=0.10.0'} - - to-regex-range@2.1.1: - resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} - engines: {node: '>=0.10.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - to-regex@3.0.2: - resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} - engines: {node: '>=0.10.0'} - - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - tr46@2.1.0: - resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} - engines: {node: '>=8'} - - ts-patch@1.4.5: - resolution: {integrity: sha512-8H1lIhT8uNm2TH+fk8FR1DWSyNIZ4E46aCXrUY+6mgzwiOnrqfHwb3sFGX23NR+s/vthyRxxsIZykqfGJVLvsg==} - hasBin: true - peerDependencies: - typescript: '>2.7.0' + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - deprecated: unmaintained + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + treeify@1.1.0: + resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} + engines: {node: '>=0.6'} - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + treeverse@3.0.0: + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} 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' + typescript: '>=4.8.4' + + tuf-js@4.1.0: + resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} + engines: {node: ^20.17.0 || >=22.9.0} tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -3414,66 +2565,74 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - - typescript-transform-paths@3.5.6: - resolution: {integrity: sha512-3eQTG6Ogt+pgPEh45uX2s9OwcfAVjWnyNO+osjYcOqYaWDVMIFUkqW8e0O1cOaVwdMqQFQf6alDT+76xmeS2Ag==} + typescript-eslint@8.71.0: + resolution: {integrity: sha512-fBdHYiqQ14RW6mOMXD14Svn82ZsCYAoQSzGRzyEjR59S5A2Krh/l7fGTOQ7iCr8gGy/mHVXtEF7s5fgjEdV0Pw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=3.6.5' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true - typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - union-value@1.0.1: - resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} - engines: {node: '>=0.10.0'} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} + undici@6.29.1: + resolution: {integrity: sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==} + engines: {node: '>=18.17'} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unset-value@1.0.0: - resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} - engines: {node: '>=0.10.0'} + unplugin-dts@1.1.1: + resolution: {integrity: sha512-Z2VcabC0hjYuzF62oWmZXVoxagkM8bUWgqUy+dotoJTPnooR5mNaxbpv63JCmcaxdGCW2Bb75omK6FOryOYCRg==} + peerDependencies: + '@microsoft/api-extractor': '>=7' + '@rspack/core': ^1 + '@vue/language-core': ^3.1.5 + esbuild: '*' + rolldown: '*' + rollup: '>=3' + typescript: '>=4' + vite: ^8.3.3 + webpack: ^4 || ^5 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@rspack/core': + optional: true + '@vue/language-core': + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} @@ -3488,165 +2647,110 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - urix@0.1.0: - resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} - deprecated: Please see https://github.com/lydell/urix#deprecated - - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - - use@3.1.1: - resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} - engines: {node: '>=0.10.0'} - utif@3.1.0: resolution: {integrity: sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). - hasBin: true - - v8-to-istanbul@7.1.2: - resolution: {integrity: sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==} - engines: {node: '>=10.10.0'} - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - validator@13.15.35: - resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} - engines: {node: '>= 0.10'} + validate-npm-package-name@7.0.2: + resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} + engines: {node: ^20.17.0 || >=22.9.0} verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - vite-plugin-dts@3.9.1: - resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} - engines: {node: ^14.18.0 || >=16.0.0} + vite-plugin-dts@5.1.1: + resolution: {integrity: sha512-U5dyX3DPFvHB+gaJIULn90kKGsXNYllyJ3vn491Y+LGu2nL5Fb8z7YgurN/Pi6ZFlnGljh2wSM0ZbFogSh4yAg==} peerDependencies: - typescript: '*' - vite: '*' + '@microsoft/api-extractor': '>=7' + rollup: '>=3' + vite: ^8.3.3 peerDependenciesMeta: - vite: + '@microsoft/api-extractor': + optional: true + rollup: optional: true - - vite-tsconfig-paths@4.3.2: - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: vite: optional: true - vite@4.5.14: - resolution: {integrity: sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==} - engines: {node: ^14.18.0 || >=16.0.0} + vite@8.3.5: + resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': '>= 14' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 || ^0.8.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true - less: + '@vitejs/devtools': + optional: true + esbuild: optional: true - lightningcss: + jiti: + optional: true + less: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - vue-template-compiler@2.7.16: - resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} - - vue-tsc@1.8.27: - resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} - hasBin: true - peerDependencies: - typescript: '*' - - w3c-hr-time@1.0.2: - resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} - deprecated: Use your platform's native performance.now() and performance.timeOrigin. - - w3c-xmlserializer@2.0.0: - resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} - engines: {node: '>=10'} - - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - webidl-conversions@5.0.0: - resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} - engines: {node: '>=8'} - - webidl-conversions@6.1.0: - resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} - engines: {node: '>=10.4'} - - whatwg-encoding@1.0.5: - resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - - whatwg-mimetype@2.3.0: - resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - whatwg-url@8.7.0: - resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} - engines: {node: '>=10'} + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} ws@7.5.13: resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} @@ -3660,44 +2764,32 @@ packages: utf-8-validate: optional: true - xml-name-validator@3.0.0: - resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} - - xmlbuilder@15.1.1: - resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} - engines: {node: '>=8.0'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yauzl@3.4.0: + resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} + engines: {node: '>=12'} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - z-schema@5.0.5: - resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} - engines: {node: '>=8.0.0'} - hasBin: true + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^4.6.6 + + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} snapshots: @@ -3763,8 +2855,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-string-parser@7.29.7': {} '@babel/helper-validator-identifier@7.29.7': {} @@ -3780,523 +2870,500 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7(supports-color@8.1.1))': + '@babel/template@7.29.7': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': + '@babel/traverse@7.29.8(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7(supports-color@8.1.1))': + '@babel/types@7.29.8': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7(supports-color@8.1.1))': + '@cacheable/memory@2.2.0': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 - '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))': + '@cacheable/utils@2.5.0': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + hashery: 1.5.1 + keyv: 5.6.0 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7(supports-color@8.1.1))': + '@cypress/request@4.0.1': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 4.0.6 + http-signature: 1.4.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + performance-now: 2.1.0 + qs: 6.16.0 + safe-buffer: 5.2.1 + tough-cookie: 5.1.2 + tunnel-agent: 0.6.0 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': + '@cypress/xvfb@1.2.4(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + debug: 3.2.7(supports-color@8.1.1) + lodash.once: 4.1.1 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7(supports-color@8.1.1))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + eslint-visitor-keys: 3.4.3 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint-community/regexpp@4.12.2': {} - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7(supports-color@8.1.1))': + '@eslint/config-array@0.23.5(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint/object-schema': 3.0.5 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': + '@eslint/config-helpers@0.7.0': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint/core': 1.2.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7(supports-color@8.1.1))': + '@eslint/core@1.2.1': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@types/json-schema': 7.0.15 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7(supports-color@8.1.1))': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint/js@10.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))': + optionalDependencies: + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7(supports-color@8.1.1))': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint/object-schema@3.0.5': {} - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))': + '@eslint/plugin-kit@0.7.3': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@gar/promise-retry@1.0.3': {} - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))': + '@humanfs/core@0.19.2': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 + '@humanfs/types': 0.15.0 - '@babel/template@7.29.7': + '@humanfs/node@0.16.8': dependencies: - '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 - '@babel/traverse@7.29.8(supports-color@8.1.1)': + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9)(supports-color@8.1.1)': dependencies: - '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 - '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.9 - '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8(supports-color@8.1.1) '@babel/types': 7.29.8 - debug: 4.4.3(supports-color@8.1.1) + prettier: 3.9.9 + semver: 7.8.5 transitivePeerDependencies: - supports-color - '@babel/types@7.29.8': + '@isaacs/fs-minipass@4.0.1': dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - - '@bcoe/v8-coverage@0.2.3': {} + minipass: 7.1.3 - '@cnakazawa/watch@1.0.4': - dependencies: - exec-sh: 0.3.6 - minimist: 1.2.8 + '@isaacs/string-locale-compare@1.1.0': {} - '@colors/colors@1.5.0': + '@j178/prek-android-arm64@0.5.4': optional: true - '@cypress/react@7.0.3(@types/react@19.3.0)(cypress@12.17.4)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': - dependencies: - cypress: 12.17.4 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - optionalDependencies: - '@types/react': 19.3.0 - - '@cypress/request@2.88.12': - dependencies: - aws-sign2: 0.7.0 - aws4: 1.13.2 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - http-signature: 1.3.6 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - performance-now: 2.1.0 - qs: 6.10.7 - safe-buffer: 5.2.1 - tough-cookie: 4.1.4 - tunnel-agent: 0.6.0 - uuid: 8.3.2 - - '@cypress/vite-dev-server@2.2.3(supports-color@8.1.1)(vite@4.5.14(@types/node@20.19.43))': - dependencies: - debug: 4.4.3(supports-color@8.1.1) - get-port: 5.1.1 - vite: 4.5.14(@types/node@20.19.43) - transitivePeerDependencies: - - supports-color - - '@cypress/xvfb@1.2.4(supports-color@8.1.1)': - dependencies: - debug: 3.2.7(supports-color@8.1.1) - lodash.once: 4.1.1 - transitivePeerDependencies: - - supports-color - - '@esbuild/android-arm64@0.18.20': + '@j178/prek-darwin-arm64@0.5.4': optional: true - '@esbuild/android-arm@0.18.20': + '@j178/prek-darwin-x64@0.5.4': optional: true - '@esbuild/android-x64@0.18.20': + '@j178/prek-linux-arm64-gnu@0.5.4': optional: true - '@esbuild/darwin-arm64@0.18.20': + '@j178/prek-linux-arm64-musl@0.5.4': optional: true - '@esbuild/darwin-x64@0.18.20': + '@j178/prek-linux-x64-gnu@0.5.4': optional: true - '@esbuild/freebsd-arm64@0.18.20': + '@j178/prek-linux-x64-musl@0.5.4': optional: true - '@esbuild/freebsd-x64@0.18.20': + '@j178/prek-win32-arm64-msvc@0.5.4': optional: true - '@esbuild/linux-arm64@0.18.20': + '@j178/prek-win32-x64-msvc@0.5.4': optional: true - '@esbuild/linux-arm@0.18.20': - optional: true + '@j178/prek@0.5.4': + optionalDependencies: + '@j178/prek-android-arm64': 0.5.4 + '@j178/prek-darwin-arm64': 0.5.4 + '@j178/prek-darwin-x64': 0.5.4 + '@j178/prek-linux-arm64-gnu': 0.5.4 + '@j178/prek-linux-arm64-musl': 0.5.4 + '@j178/prek-linux-x64-gnu': 0.5.4 + '@j178/prek-linux-x64-musl': 0.5.4 + '@j178/prek-win32-arm64-msvc': 0.5.4 + '@j178/prek-win32-x64-msvc': 0.5.4 - '@esbuild/linux-ia32@0.18.20': - optional: true + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 - '@esbuild/linux-loong64@0.18.20': - optional: true + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@esbuild/linux-mips64el@0.18.20': - optional: true + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@microsoft/api-extractor-model@7.33.14(@types/node@24.19.0)': + dependencies: + '@microsoft/tsdoc': 0.17.0 + '@microsoft/tsdoc-config': 0.18.2 + '@rushstack/node-core-library': 5.25.0(@types/node@24.19.0) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.59.3(@types/node@24.19.0)': + dependencies: + '@microsoft/api-extractor-model': 7.33.14(@types/node@24.19.0) + '@microsoft/tsdoc': 0.17.0 + '@microsoft/tsdoc-config': 0.18.2 + '@rushstack/node-core-library': 5.25.0(@types/node@24.19.0) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.6(@types/node@24.19.0) + '@rushstack/ts-command-line': 5.3.16(@types/node@24.19.0) + diff: 8.0.4 + minimatch: 10.2.3 + resolve: 1.22.12 + semver: 7.7.4 + source-map: 0.6.1 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/tsdoc-config@0.18.2': + dependencies: + '@microsoft/tsdoc': 0.17.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.12 + + '@microsoft/tsdoc@0.17.0': {} - '@esbuild/linux-ppc64@0.18.20': + '@napi-rs/canvas-android-arm64@1.0.9': optional: true - '@esbuild/linux-riscv64@0.18.20': + '@napi-rs/canvas-darwin-arm64@1.0.9': optional: true - '@esbuild/linux-s390x@0.18.20': + '@napi-rs/canvas-darwin-x64@1.0.9': optional: true - '@esbuild/linux-x64@0.18.20': + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.9': optional: true - '@esbuild/netbsd-x64@0.18.20': + '@napi-rs/canvas-linux-arm64-gnu@1.0.9': optional: true - '@esbuild/openbsd-x64@0.18.20': + '@napi-rs/canvas-linux-arm64-musl@1.0.9': optional: true - '@esbuild/sunos-x64@0.18.20': + '@napi-rs/canvas-linux-riscv64-gnu@1.0.9': optional: true - '@esbuild/win32-arm64@0.18.20': + '@napi-rs/canvas-linux-x64-gnu@1.0.9': optional: true - '@esbuild/win32-ia32@0.18.20': + '@napi-rs/canvas-linux-x64-musl@1.0.9': optional: true - '@esbuild/win32-x64@0.18.20': + '@napi-rs/canvas-win32-arm64-msvc@1.0.9': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@8.57.1(supports-color@8.1.1))': - dependencies: - eslint: 8.57.1(supports-color@8.1.1) - eslint-visitor-keys: 3.4.3 + '@napi-rs/canvas-win32-x64-msvc@1.0.9': + optional: true - '@eslint-community/regexpp@4.12.2': {} + '@napi-rs/canvas@1.0.9': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 1.0.9 + '@napi-rs/canvas-darwin-arm64': 1.0.9 + '@napi-rs/canvas-darwin-x64': 1.0.9 + '@napi-rs/canvas-linux-arm-gnueabihf': 1.0.9 + '@napi-rs/canvas-linux-arm64-gnu': 1.0.9 + '@napi-rs/canvas-linux-arm64-musl': 1.0.9 + '@napi-rs/canvas-linux-riscv64-gnu': 1.0.9 + '@napi-rs/canvas-linux-x64-gnu': 1.0.9 + '@napi-rs/canvas-linux-x64-musl': 1.0.9 + '@napi-rs/canvas-win32-arm64-msvc': 1.0.9 + '@napi-rs/canvas-win32-x64-msvc': 1.0.9 + optional: true - '@eslint/eslintrc@2.1.4(supports-color@8.1.1)': + '@npmcli/agent@4.0.2(supports-color@8.1.1)': dependencies: - ajv: 6.15.0 - debug: 4.4.3(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.3.2 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 + agent-base: 7.1.4 + http-proxy-agent: 7.0.2(supports-color@8.1.1) + https-proxy-agent: 7.0.6(supports-color@8.1.1) + lru-cache: 11.5.3 + socks-proxy-agent: 8.0.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@eslint/js@8.57.1': {} - - '@humanwhocodes/config-array@0.13.0(supports-color@8.1.1)': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3(supports-color@8.1.1) - minimatch: 3.1.5 + '@npmcli/arborist@9.6.0(supports-color@8.1.1)': + dependencies: + '@gar/promise-retry': 1.0.3 + '@isaacs/string-locale-compare': 1.1.0 + '@npmcli/fs': 5.0.0 + '@npmcli/installed-package-contents': 4.0.0 + '@npmcli/map-workspaces': 5.0.3 + '@npmcli/metavuln-calculator': 9.0.3(supports-color@8.1.1) + '@npmcli/name-from-folder': 4.0.0 + '@npmcli/node-gyp': 5.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/query': 5.0.0 + '@npmcli/redact': 4.0.0 + '@npmcli/run-script': 10.0.4 + bin-links: 6.0.2 + cacache: 20.0.4 + common-ancestor-path: 2.0.0 + hosted-git-info: 9.0.3 + json-stringify-nice: 1.1.4 + lru-cache: 11.5.3 + minimatch: 10.2.6 + nopt: 9.0.0 + npm-install-checks: 8.0.0 + npm-package-arg: 13.0.2 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1(supports-color@8.1.1) + pacote: 21.5.1(supports-color@8.1.1) + parse-conflict-json: 5.0.1 + proc-log: 6.1.0 + proggy: 4.0.0 + promise-all-reject-late: 1.0.1 + promise-call-limit: 3.0.2 + semver: 7.8.5 + ssri: 13.0.1 + treeverse: 3.0.0 + walk-up-path: 4.0.0 transitivePeerDependencies: - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} + '@npmcli/fs@5.0.0': + dependencies: + semver: 7.8.5 - '@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@2.8.8)(supports-color@8.1.1)': + '@npmcli/git@7.0.2': dependencies: - '@babel/generator': 7.29.8 - '@babel/parser': 7.29.9 - '@babel/traverse': 7.29.8(supports-color@8.1.1) - '@babel/types': 7.29.8 - prettier: 2.8.8 + '@gar/promise-retry': 1.0.3 + '@npmcli/promise-spawn': 9.0.1 + ini: 6.0.0 + lru-cache: 11.5.3 + npm-pick-manifest: 11.0.3 + proc-log: 6.1.0 semver: 7.8.5 - transitivePeerDependencies: - - supports-color + which: 6.0.1 - '@istanbuljs/load-nyc-config@1.1.0': + '@npmcli/installed-package-contents@4.0.0': dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.15.2 - resolve-from: 5.0.0 + npm-bundled: 5.0.0 + npm-normalize-package-bin: 5.0.0 - '@istanbuljs/schema@0.1.6': {} + '@npmcli/map-workspaces@5.0.3': + dependencies: + '@npmcli/name-from-folder': 4.0.0 + '@npmcli/package-json': 7.0.5 + glob: 13.0.6 + minimatch: 10.2.6 - '@jest/console@26.6.2': + '@npmcli/metavuln-calculator@9.0.3(supports-color@8.1.1)': dependencies: - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - chalk: 4.1.2 - jest-message-util: 26.6.2 - jest-util: 26.6.2 - slash: 3.0.0 - - '@jest/core@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1)': - dependencies: - '@jest/console': 26.6.2 - '@jest/reporters': 26.6.2(supports-color@8.1.1) - '@jest/test-result': 26.6.2 - '@jest/transform': 26.6.2(supports-color@8.1.1) - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 26.6.2 - jest-config: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-message-util: 26.6.2 - jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 - jest-resolve-dependencies: 26.6.3(supports-color@8.1.1) - jest-runner: 26.6.3(supports-color@8.1.1) - jest-runtime: 26.6.3(supports-color@8.1.1) - jest-snapshot: 26.6.2(supports-color@8.1.1) - jest-util: 26.6.2 - jest-validate: 26.6.2 - jest-watcher: 26.6.2 - micromatch: 4.0.8 - p-each-series: 2.2.0 - rimraf: 3.0.2 - slash: 3.0.0 - strip-ansi: 6.0.1 + cacache: 20.0.4 + json-parse-even-better-errors: 5.0.0 + pacote: 21.5.1(supports-color@8.1.1) + proc-log: 6.1.0 + semver: 7.8.5 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - - ts-node - - utf-8-validate - '@jest/environment@26.6.2': - dependencies: - '@jest/fake-timers': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - jest-mock: 26.6.2 + '@npmcli/name-from-folder@4.0.0': {} - '@jest/fake-timers@26.6.2': - dependencies: - '@jest/types': 26.6.2 - '@sinonjs/fake-timers': 6.0.1 - '@types/node': 20.19.43 - jest-message-util: 26.6.2 - jest-mock: 26.6.2 - jest-util: 26.6.2 + '@npmcli/node-gyp@5.0.0': {} - '@jest/globals@26.6.2': + '@npmcli/package-json@7.0.5': dependencies: - '@jest/environment': 26.6.2 - '@jest/types': 26.6.2 - expect: 26.6.2 + '@npmcli/git': 7.0.2 + glob: 13.0.6 + hosted-git-info: 9.0.3 + json-parse-even-better-errors: 5.0.0 + proc-log: 6.1.0 + semver: 7.8.5 + spdx-expression-parse: 4.0.0 - '@jest/reporters@26.6.2(supports-color@8.1.1)': + '@npmcli/promise-spawn@9.0.1': dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 26.6.2 - '@jest/test-result': 26.6.2 - '@jest/transform': 26.6.2(supports-color@8.1.1) - '@jest/types': 26.6.2 - chalk: 4.1.2 - collect-v8-coverage: 1.0.3 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 4.0.3(supports-color@8.1.1) - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1(supports-color@8.1.1) - istanbul-reports: 3.2.0 - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-resolve: 26.6.2 - jest-util: 26.6.2 - jest-worker: 26.6.2 - slash: 3.0.0 - source-map: 0.6.1 - string-length: 4.0.2 - terminal-link: 2.1.1 - v8-to-istanbul: 7.1.2 - optionalDependencies: - node-notifier: 8.0.2 - transitivePeerDependencies: - - supports-color + which: 6.0.1 - '@jest/source-map@26.6.2': + '@npmcli/query@5.0.0': dependencies: - callsites: 3.1.0 - graceful-fs: 4.2.11 - source-map: 0.6.1 + postcss-selector-parser: 7.1.6 - '@jest/test-result@26.6.2': - dependencies: - '@jest/console': 26.6.2 - '@jest/types': 26.6.2 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 + '@npmcli/redact@4.0.0': {} - '@jest/test-sequencer@26.6.3(supports-color@8.1.1)': + '@npmcli/run-script@10.0.4': dependencies: - '@jest/test-result': 26.6.2 - graceful-fs: 4.2.11 - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-runner: 26.6.3(supports-color@8.1.1) - jest-runtime: 26.6.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color + '@npmcli/node-gyp': 5.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/promise-spawn': 9.0.1 + node-gyp: 12.4.0 + proc-log: 6.1.0 - '@jest/transform@26.6.2(supports-color@8.1.1)': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@jest/types': 26.6.2 - babel-plugin-istanbul: 6.1.1(supports-color@8.1.1) - chalk: 4.1.2 - convert-source-map: 1.9.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-regex-util: 26.0.0 - jest-util: 26.6.2 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - source-map: 0.6.1 - write-file-atomic: 3.0.3 - transitivePeerDependencies: - - supports-color + '@oxc-project/types@0.151.0': + optional: true - '@jest/types@26.6.2': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 20.19.43 - '@types/yargs': 15.0.20 - chalk: 4.1.2 + '@oxc-project/types@0.155.0': {} - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - '@jridgewell/trace-mapping': 0.3.31 + '@rolldown/binding-android-arm-eabi@1.2.11': + optional: true - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@rolldown/binding-android-arm-eabi@1.2.15': + optional: true - '@jridgewell/resolve-uri@3.1.2': {} + '@rolldown/binding-android-arm64@1.2.11': + optional: true - '@jridgewell/sourcemap-codec@1.6.0': {} + '@rolldown/binding-android-arm64@1.2.15': + optional: true - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 + '@rolldown/binding-darwin-arm64@1.2.11': + optional: true - '@mapbox/node-pre-gyp@1.0.11(supports-color@8.1.1)': - dependencies: - detect-libc: 2.1.2 - https-proxy-agent: 5.0.1(supports-color@8.1.1) - make-dir: 3.1.0 - node-fetch: 2.7.0 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.8.5 - tar: 6.2.1 - transitivePeerDependencies: - - encoding - - supports-color + '@rolldown/binding-darwin-arm64@1.2.15': optional: true - '@microsoft/api-extractor-model@7.28.13(@types/node@20.19.43)': - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.19.43) - transitivePeerDependencies: - - '@types/node' + '@rolldown/binding-darwin-x64@1.2.11': + optional: true - '@microsoft/api-extractor@7.43.0(@types/node@20.19.43)': - dependencies: - '@microsoft/api-extractor-model': 7.28.13(@types/node@20.19.43) - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.19.43) - '@rushstack/rig-package': 0.5.2 - '@rushstack/terminal': 0.10.0(@types/node@20.19.43) - '@rushstack/ts-command-line': 4.19.1(@types/node@20.19.43) - lodash: 4.17.23 - minimatch: 3.0.8 - resolve: 1.22.12 - semver: 7.5.4 - source-map: 0.6.1 - typescript: 5.4.2 - transitivePeerDependencies: - - '@types/node' + '@rolldown/binding-darwin-x64@1.2.15': + optional: true - '@microsoft/tsdoc-config@0.16.2': - dependencies: - '@microsoft/tsdoc': 0.14.2 - ajv: 6.12.6 - jju: 1.4.0 - resolve: 1.19.0 + '@rolldown/binding-freebsd-x64@1.2.11': + optional: true - '@microsoft/tsdoc@0.14.2': {} + '@rolldown/binding-freebsd-x64@1.2.15': + optional: true - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + optional: true - '@nodelib/fs.stat@2.0.5': {} + '@rolldown/binding-linux-arm-gnueabihf@1.2.15': + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 + '@rolldown/binding-linux-arm64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.15': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.15': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.15': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.15': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.15': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.15': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.11': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.15': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.11': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.15': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.11': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.15': + optional: true - '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.4.0(rollup@3.30.0)': dependencies: @@ -4306,105 +3373,109 @@ snapshots: optionalDependencies: rollup: 3.30.0 - '@rushstack/node-core-library@4.0.2(@types/node@20.19.43)': + '@rushstack/node-core-library@5.25.0(@types/node@24.19.0)': dependencies: - fs-extra: 7.0.1 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-formats: 3.0.1 + fs-extra: 11.3.6 import-lazy: 4.0.0 jju: 1.4.0 resolve: 1.22.12 - semver: 7.5.4 - z-schema: 5.0.5 + semver: 7.7.4 + optionalDependencies: + '@types/node': 24.19.0 + + '@rushstack/problem-matcher@0.2.1(@types/node@24.19.0)': optionalDependencies: - '@types/node': 20.19.43 + '@types/node': 24.19.0 - '@rushstack/rig-package@0.5.2': + '@rushstack/rig-package@0.7.3': dependencies: + jju: 1.4.0 resolve: 1.22.12 - strip-json-comments: 3.1.1 - '@rushstack/terminal@0.10.0(@types/node@20.19.43)': + '@rushstack/terminal@0.24.6(@types/node@24.19.0)': dependencies: - '@rushstack/node-core-library': 4.0.2(@types/node@20.19.43) + '@rushstack/node-core-library': 5.25.0(@types/node@24.19.0) + '@rushstack/problem-matcher': 0.2.1(@types/node@24.19.0) supports-color: 8.1.1 optionalDependencies: - '@types/node': 20.19.43 + '@types/node': 24.19.0 - '@rushstack/ts-command-line@4.19.1(@types/node@20.19.43)': + '@rushstack/ts-command-line@5.3.16(@types/node@24.19.0)': dependencies: - '@rushstack/terminal': 0.10.0(@types/node@20.19.43) + '@rushstack/terminal': 0.24.6(@types/node@24.19.0) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 transitivePeerDependencies: - '@types/node' - '@sinonjs/commons@1.8.6': + '@sigstore/bundle@4.0.0': dependencies: - type-detect: 4.0.8 + '@sigstore/protobuf-specs': 0.5.2 - '@sinonjs/fake-timers@6.0.1': - dependencies: - '@sinonjs/commons': 1.8.6 - - '@tootallnate/once@1.1.2': {} + '@sigstore/core@3.2.1': {} - '@types/argparse@1.0.38': {} + '@sigstore/protobuf-specs@0.5.2': {} - '@types/babel__core@7.20.5': + '@sigstore/sign@4.1.1(supports-color@8.1.1)': dependencies: - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 + '@gar/promise-retry': 1.0.3 + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.2 + make-fetch-happen: 15.0.6(supports-color@8.1.1) + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color - '@types/babel__generator@7.27.0': + '@sigstore/tuf@4.0.2(supports-color@8.1.1)': dependencies: - '@babel/types': 7.29.8 + '@sigstore/protobuf-specs': 0.5.2 + tuf-js: 4.1.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color - '@types/babel__template@7.4.4': + '@sigstore/verify@3.1.1': dependencies: - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.2 - '@types/babel__traverse@7.28.0': + '@simonsmith/cypress-image-snapshot@11.0.0(cypress@16.1.0)': dependencies: - '@babel/types': 7.29.8 - - '@types/cypress-image-snapshot@3.1.9': {} + '@types/pixelmatch': 5.2.6 + chalk: 4.1.2 + cypress: 16.1.0 + jest-image-snapshot: 6.5.2 + ssim.js: 3.5.0 + transitivePeerDependencies: + - jest - '@types/estree@1.0.9': {} + '@tufjs/canonical-json@2.0.0': {} - '@types/graceful-fs@4.1.9': + '@tufjs/models@4.1.0': dependencies: - '@types/node': 20.19.43 - - '@types/istanbul-lib-coverage@2.0.6': {} + '@tufjs/canonical-json': 2.0.0 + minimatch: 10.2.6 - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 + '@types/argparse@1.0.38': {} - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 + '@types/esrecurse@4.3.1': {} - '@types/jest@27.5.2': - dependencies: - jest-matcher-utils: 27.5.1 - pretty-format: 27.5.1 + '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} - '@types/node@16.18.126': {} - - '@types/node@20.19.43': + '@types/node@24.19.0': dependencies: - undici-types: 6.21.0 + undici-types: 7.24.6 - '@types/normalize-package-data@2.4.4': {} - - '@types/prettier@2.7.3': {} + '@types/pixelmatch@5.2.6': + dependencies: + '@types/node': 24.19.0 '@types/react-dom@19.3.0(@types/react@19.3.0)': dependencies: @@ -4414,212 +3485,145 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/semver@7.8.0': {} - '@types/sinonjs__fake-timers@8.1.1': {} '@types/sizzle@2.3.10': {} - '@types/stack-utils@2.0.3': {} + '@types/tmp@0.2.6': {} '@types/utif@3.0.6': dependencies: - '@types/node': 20.19.43 - - '@types/uuid@8.3.4': {} - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@15.0.20': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 20.19.43 - optional: true + '@types/node': 24.19.0 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5)': + '@typescript-eslint/eslint-plugin@8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) - debug: 4.4.3(supports-color@8.1.1) - eslint: 8.57.1(supports-color@8.1.1) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare-lite: 1.4.1 - semver: 7.8.5 - tsutils: 3.21.0(typescript@4.9.5) - optionalDependencies: - typescript: 4.9.5 + '@typescript-eslint/parser': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/type-utils': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.71.0 + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + ignore: 7.0.10 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5)': + '@typescript-eslint/parser@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(supports-color@8.1.1)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.71.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 8.57.1(supports-color@8.1.1) - optionalDependencies: - typescript: 4.9.5 + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@5.62.0': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - - '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5)': + '@typescript-eslint/project-service@8.71.0(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(supports-color@8.1.1)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) + '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3) + '@typescript-eslint/types': 8.71.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 8.57.1(supports-color@8.1.1) - tsutils: 3.21.0(typescript@4.9.5) - optionalDependencies: - typescript: 4.9.5 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@5.62.0': {} + '@typescript-eslint/scope-manager@8.71.0': + dependencies: + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/visitor-keys': 8.71.0 + + '@typescript-eslint/tsconfig-utils@8.71.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 - '@typescript-eslint/typescript-estree@5.62.0(supports-color@8.1.1)(typescript@4.9.5)': + '@typescript-eslint/type-utils@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.8.5 - tsutils: 3.21.0(typescript@4.9.5) - optionalDependencies: - typescript: 4.9.5 + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5)': + '@typescript-eslint/types@8.71.0': {} + + '@typescript-eslint/typescript-estree@8.71.0(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1(supports-color@8.1.1)) - '@types/json-schema': 7.0.15 - '@types/semver': 7.8.0 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(supports-color@8.1.1)(typescript@4.9.5) - eslint: 8.57.1(supports-color@8.1.1) - eslint-scope: 5.1.1 + '@typescript-eslint/project-service': 8.71.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3) + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/visitor-keys': 8.71.0 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.3 semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - - typescript - - '@typescript-eslint/visitor-keys@5.62.0': - dependencies: - '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.3 - '@ungap/structured-clone@1.4.0': {} - - '@vitejs/plugin-react@4.7.0(supports-color@8.1.1)(vite@4.5.14(@types/node@20.19.43))': + '@typescript-eslint/utils@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) - '@rolldown/pluginutils': 1.0.0-beta.27 - '@types/babel__core': 7.20.5 - react-refresh: 0.17.0 - vite: 4.5.14(@types/node@20.19.43) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1)) + '@typescript-eslint/scope-manager': 8.71.0 + '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/typescript-estree': 8.71.0(supports-color@8.1.1)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@volar/language-core@1.11.1': + '@typescript-eslint/visitor-keys@8.71.0': dependencies: - '@volar/source-map': 1.11.1 + '@typescript-eslint/types': 8.71.0 + eslint-visitor-keys: 5.0.1 - '@volar/source-map@1.11.1': + '@vitejs/plugin-react@6.1.1(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0))': dependencies: - muggle-string: 0.3.1 + '@rolldown/pluginutils': 1.0.1 + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) - '@volar/typescript@1.11.1(typescript@4.9.5)': + '@volar/language-core@2.4.28': dependencies: - '@volar/language-core': 1.11.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 4.9.5 - - '@vue/compiler-core@3.5.43': - dependencies: - '@babel/parser': 7.29.9 - '@vue/shared': 3.5.43 - entities: 7.0.1 - estree-walker: 2.0.2 - source-map-js: 1.2.1 + '@volar/source-map': 2.4.28 - '@vue/compiler-dom@3.5.43': - dependencies: - '@vue/compiler-core': 3.5.43 - '@vue/shared': 3.5.43 + '@volar/source-map@2.4.28': {} - '@vue/language-core@1.8.27(typescript@4.9.5)': + '@volar/typescript@2.4.28(typescript@6.0.3)': dependencies: - '@volar/language-core': 1.11.1 - '@volar/source-map': 1.11.1 - '@vue/compiler-dom': 3.5.43 - '@vue/shared': 3.5.43 - computeds: 0.0.1 - minimatch: 9.0.9 - muggle-string: 0.3.1 + '@volar/language-core': 2.4.28 path-browserify: 1.0.1 - vue-template-compiler: 2.7.16 + vscode-uri: 3.2.0 optionalDependencies: - typescript: 4.9.5 - - '@vue/shared@3.5.43': {} + typescript: 6.0.3 - '@xmldom/xmldom@0.9.12': {} + abbrev@2.0.0: {} - abab@2.0.6: {} - - abbrev@1.1.1: - optional: true - - acorn-globals@6.0.0: - dependencies: - acorn: 7.4.1 - acorn-walk: 7.2.0 + abbrev@4.0.0: {} acorn-jsx@5.3.2(acorn@8.18.0): dependencies: acorn: 8.18.0 - acorn-walk@7.2.0: {} - - acorn@7.4.1: {} - acorn@8.18.0: {} - agent-base@6.0.2(supports-color@8.1.1): - dependencies: - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color + agent-base@7.1.4: {} - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 - ajv@6.12.6: + ajv-formats@3.0.1: dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 + ajv: 8.20.0 ajv@6.15.0: dependencies: @@ -4628,70 +3632,41 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-colors@4.1.3: {} + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.9 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 - ansi-escapes@4.3.2: + ajv@8.20.0: dependencies: - type-fest: 0.21.3 + fast-deep-equal: 3.1.3 + fast-uri: 3.1.9 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 - ansi-regex@2.1.1: {} + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 ansi-regex@5.0.1: {} - ansi-styles@2.2.1: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 + ansi-regex@6.4.0: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - - anymatch@2.0.0(supports-color@8.1.1): - dependencies: - micromatch: 3.1.10(supports-color@8.1.1) - normalize-path: 2.1.1 - transitivePeerDependencies: - - supports-color - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - - app-path@3.3.0: - dependencies: - execa: 1.0.0 - - aproba@2.1.0: - optional: true - - arch@2.2.0: {} + ansi-styles@6.2.3: {} - are-we-there-yet@2.0.0: - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - optional: true + arch@3.0.0: {} argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - argparse@2.0.1: {} - - arr-diff@4.0.0: {} - - arr-flatten@1.1.0: {} - - arr-union@3.1.0: {} - - array-union@2.1.0: {} - - array-unique@0.3.2: {} + array-find-index@1.0.2: {} asn1@0.2.6: dependencies: @@ -4699,131 +3674,39 @@ snapshots: assert-plus@1.0.0: {} - assign-symbols@1.0.0: {} - - astral-regex@2.0.0: {} - - async@3.2.6: {} - asynckit@0.4.0: {} at-least-node@1.0.0: {} - atob@2.1.2: {} - aws-sign2@0.7.0: {} aws4@1.13.2: {} - babel-jest@26.6.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@jest/transform': 26.6.2(supports-color@8.1.1) - '@jest/types': 26.6.2 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1(supports-color@8.1.1) - babel-preset-jest: 26.6.2(@babel/core@7.29.7(supports-color@8.1.1)) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1(supports-color@8.1.1): - dependencies: - '@babel/helper-plugin-utils': 7.29.7 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.6 - istanbul-lib-instrument: 5.2.1(supports-color@8.1.1) - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@26.6.2: - dependencies: - '@babel/template': 7.29.7 - '@babel/types': 7.29.8 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.28.0 - - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7(supports-color@8.1.1)): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7(supports-color@8.1.1)) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7(supports-color@8.1.1)) - - babel-preset-jest@26.6.2(@babel/core@7.29.7(supports-color@8.1.1)): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - babel-plugin-jest-hoist: 26.6.2 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7(supports-color@8.1.1)) - - balanced-match@1.0.2: {} + balanced-match@4.0.4: {} base64-js@1.5.1: {} - base@0.11.2: - dependencies: - cache-base: 1.0.1 - class-utils: 0.3.6 - component-emitter: 1.3.1 - define-property: 1.0.0 - isobject: 3.0.1 - mixin-deep: 1.3.2 - pascalcase: 0.1.1 - baseline-browser-mapping@2.11.26: {} bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 - blob-util@2.0.2: {} - - bluebird@3.7.2: {} - - brace-expansion@1.1.21: + bin-links@6.0.2: dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 + cmd-shim: 8.0.0 + npm-normalize-package-bin: 5.0.0 + proc-log: 6.1.0 + read-cmd-shim: 6.0.0 + write-file-atomic: 7.0.1 - brace-expansion@2.1.7: - dependencies: - balanced-match: 1.0.2 + blob-util@2.0.2: {} - braces@2.3.2(supports-color@8.1.1): - dependencies: - arr-flatten: 1.1.0 - array-unique: 0.3.2 - extend-shallow: 2.0.1 - fill-range: 4.0.0 - isobject: 3.0.1 - repeat-element: 1.1.4 - snapdragon: 0.8.2(supports-color@8.1.1) - snapdragon-node: 2.1.1 - split-string: 3.1.0 - to-regex: 3.0.2 - transitivePeerDependencies: - - supports-color + bluebird@3.7.2: {} - braces@3.0.3: + brace-expansion@5.0.12: dependencies: - fill-range: 7.1.1 - - browser-process-hrtime@1.0.0: {} + balanced-match: 4.0.4 browserslist@4.29.2: dependencies: @@ -4833,30 +3716,31 @@ snapshots: node-releases: 2.0.57 update-browserslist-db: 1.3.3(browserslist@4.29.2) - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-crc32@0.2.13: {} - - buffer-from@1.1.2: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - cache-base@1.0.1: + cacache@20.0.4: + dependencies: + '@npmcli/fs': 5.0.0 + fs-minipass: 3.0.3 + glob: 13.0.6 + lru-cache: 11.5.3 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + p-map: 7.0.8 + ssri: 13.0.1 + + cacheable@2.5.0: dependencies: - collection-visit: 1.0.0 - component-emitter: 1.3.1 - get-value: 2.0.6 - has-value: 1.0.0 - isobject: 3.0.1 - set-value: 2.0.1 - to-object-path: 0.3.0 - union-value: 1.0.1 - unset-value: 1.0.0 + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 cachedir@2.4.0: {} @@ -4870,214 +3754,116 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - caniuse-lite@1.0.30001813: {} - canvas@2.11.2(supports-color@8.1.1): - dependencies: - '@mapbox/node-pre-gyp': 1.0.11(supports-color@8.1.1) - nan: 2.29.0 - simple-get: 3.1.1 - transitivePeerDependencies: - - encoding - - supports-color - optional: true - - capture-exit@2.0.0: - dependencies: - rsvp: 4.8.5 - caseless@0.12.0: {} - chalk@1.1.3: - dependencies: - ansi-styles: 2.2.1 - escape-string-regexp: 1.0.5 - has-ansi: 2.0.0 - strip-ansi: 3.0.1 - supports-color: 2.0.0 - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - char-regex@1.0.2: {} - - check-more-types@2.24.0: {} - - chownr@2.0.0: - optional: true - - ci-info@2.0.0: {} - - ci-info@3.9.0: {} + chownr@3.0.0: {} - cjs-module-lexer@0.6.0: {} - - class-utils@0.3.6: + chrome-remote-interface@0.33.3: dependencies: - arr-union: 3.1.0 - define-property: 0.2.5 - isobject: 3.0.1 - static-extend: 0.1.2 + commander: 2.11.0 + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - utf-8-validate - clean-stack@2.2.0: {} + ci-info@4.4.0: {} - cli-cursor@3.1.0: + cli-cursor@5.0.0: dependencies: - restore-cursor: 3.1.0 + restore-cursor: 5.1.0 - cli-table3@0.6.5: + cli-table3@0.6.1: dependencies: string-width: 4.2.3 optionalDependencies: - '@colors/colors': 1.5.0 - - cli-truncate@2.1.0: - dependencies: - slice-ansi: 3.0.0 - string-width: 4.2.3 - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - co@4.6.0: {} - - collect-v8-coverage@1.0.3: {} + colors: 1.4.0 - collection-visit@1.0.0: + cli-truncate@5.2.0: dependencies: - map-visit: 1.0.0 - object-visit: 1.0.1 + slice-ansi: 8.0.0 + string-width: 8.3.0 - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 + cmd-shim@8.0.0: {} color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} - color-support@1.1.3: - optional: true - colorette@2.0.20: {} + colors@1.4.0: + optional: true + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 + commander@2.11.0: {} + commander@6.2.1: {} - commander@9.5.0: - optional: true + common-ancestor-path@2.0.0: {} common-tags@1.8.2: {} - component-emitter@1.3.1: {} + compare-versions@6.1.1: {} - computeds@0.0.1: {} + confbox@0.1.8: {} - concat-map@0.0.1: {} + confbox@0.3.1: {} - console-control-strings@1.1.0: - optional: true - - convert-source-map@1.9.0: {} + content-type@2.1.0: {} convert-source-map@2.0.0: {} - copy-descriptor@0.1.1: {} - core-util-is@1.0.2: {} - cross-spawn@6.0.6: - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.2 - shebang-command: 1.2.0 - which: 1.3.1 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - cssom@0.3.8: {} - - cssom@0.4.4: {} - - cssstyle@2.3.0: - dependencies: - cssom: 0.3.8 + cssesc@3.0.0: {} csstype@3.2.3: {} - cypress-image-snapshot@4.0.1(cypress@12.17.4)(jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1)): - dependencies: - chalk: 2.4.2 - cypress: 12.17.4 - fs-extra: 7.0.1 - glob: 7.2.3 - jest-image-snapshot: 4.2.0(jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1)) - pkg-dir: 3.0.0 - term-img: 4.1.0 - transitivePeerDependencies: - - jest - - cypress@12.17.4: + cypress@16.1.0: dependencies: - '@cypress/request': 2.88.12 + '@cypress/request': 4.0.1 '@cypress/xvfb': 1.2.4(supports-color@8.1.1) - '@types/node': 16.18.126 '@types/sinonjs__fake-timers': 8.1.1 '@types/sizzle': 2.3.10 - arch: 2.2.0 + '@types/tmp': 0.2.6 + arch: 3.0.0 blob-util: 2.0.2 bluebird: 3.7.2 buffer: 5.7.1 cachedir: 2.4.0 chalk: 4.1.2 - check-more-types: 2.24.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.5 + chrome-remote-interface: 0.33.3 + ci-info: 4.4.0 + cli-table3: 0.6.1 commander: 6.2.1 common-tags: 1.8.2 dayjs: 1.11.23 debug: 4.4.3(supports-color@8.1.1) - enquirer: 2.4.1 eventemitter2: 6.4.7 execa: 4.1.0 executable: 4.1.1 - extract-zip: 2.0.1(supports-color@8.1.1) - figures: 3.2.0 fs-extra: 9.1.0 - getos: 3.2.1 - is-ci: 3.0.1 + hasha: 5.2.2 is-installed-globally: 0.4.0 - lazy-ass: 1.6.0 - listr2: 3.14.0(enquirer@2.4.1) + listr2: 9.0.5 lodash: 4.18.1 log-symbols: 4.1.0 minimist: 1.2.8 @@ -5086,32 +3872,22 @@ snapshots: process: 0.11.10 proxy-from-env: 1.0.0 request-progress: 3.0.0 - semver: 7.8.5 supports-color: 8.1.1 + systeminformation: 5.33.14 tmp: 0.2.7 + tree-kill: 1.2.2 untildify: 4.0.0 - yauzl: 2.10.0 + yauzl: 3.4.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate dashdash@1.14.1: dependencies: assert-plus: 1.0.0 - data-urls@2.0.0: - dependencies: - abab: 2.0.6 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - dayjs@1.11.23: {} - de-indent@1.0.2: {} - - debug@2.6.9(supports-color@8.1.1): - dependencies: - ms: 2.0.0 - optionalDependencies: - supports-color: 8.1.1 - debug@3.2.7(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -5124,59 +3900,13 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - decamelize@1.2.0: {} - - decimal.js@10.6.0: {} - - decode-uri-component@0.2.2: {} - - decompress-response@4.2.1: - dependencies: - mimic-response: 2.1.0 - optional: true - deep-is@0.1.4: {} - deepmerge@4.3.1: {} - - define-property@0.2.5: - dependencies: - is-descriptor: 0.1.8 - - define-property@1.0.0: - dependencies: - is-descriptor: 1.0.4 - - define-property@2.0.2: - dependencies: - is-descriptor: 1.0.4 - isobject: 3.0.1 - delayed-stream@1.0.0: {} - delegates@1.0.0: - optional: true - - detect-libc@2.1.2: - optional: true - - detect-newline@3.1.0: {} - - diff-sequences@26.6.2: {} - - diff-sequences@27.5.1: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 + detect-libc@2.1.2: {} - domexception@2.0.1: - dependencies: - webidl-conversions: 5.0.0 + diff@8.0.4: {} dunder-proto@1.0.1: dependencies: @@ -5191,7 +3921,7 @@ snapshots: electron-to-chromium@1.5.440: {} - emittery@0.7.2: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} @@ -5199,16 +3929,9 @@ snapshots: dependencies: once: 1.4.0 - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@7.0.1: {} + env-paths@2.2.1: {} - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 + environment@1.1.0: {} es-define-property@1.0.1: {} @@ -5225,131 +3948,78 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.4 - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} - - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-config-prettier@6.15.0(eslint@8.57.1(supports-color@8.1.1)): - dependencies: - eslint: 8.57.1(supports-color@8.1.1) - get-stdin: 6.0.0 - - eslint-plugin-prettier@3.4.1(eslint-config-prettier@6.15.0(eslint@8.57.1(supports-color@8.1.1)))(eslint@8.57.1(supports-color@8.1.1))(prettier@2.8.8): - dependencies: - eslint: 8.57.1(supports-color@8.1.1) - prettier: 2.8.8 - prettier-linter-helpers: 1.0.1 - optionalDependencies: - eslint-config-prettier: 6.15.0(eslint@8.57.1(supports-color@8.1.1)) - - eslint-plugin-unused-imports@2.0.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1)): + eslint-config-prettier@10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1)): dependencies: - eslint: 8.57.1(supports-color@8.1.1) - eslint-rule-composer: 0.3.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5))(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5) - - eslint-rule-composer@0.3.0: {} + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) - eslint-scope@5.1.1: + eslint-plugin-react-hooks@7.1.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 + '@babel/core': 7.29.7(supports-color@8.1.1) + '@babel/parser': 7.29.9 + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + hermes-parser: 0.25.1 + zod: 4.6.7 + zod-validation-error: 4.0.2(zod@4.6.7) + transitivePeerDependencies: + - supports-color - eslint-scope@7.2.2: + eslint-scope@9.1.2: dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint@8.57.1(supports-color@8.1.1): + eslint-visitor-keys@5.0.1: {} + + eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1(supports-color@8.1.1)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4(supports-color@8.1.1) - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0(supports-color@8.1.1) + '@eslint/config-array': 0.23.5(supports-color@8.1.1) + '@eslint/config-helpers': 0.7.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.3 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.4.0 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 ajv: 6.15.0 - chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@8.1.1) - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 11.1.5 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.3.2 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.5 + minimatch: 10.2.6 natural-compare: 1.4.0 optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 + optionalDependencies: + jiti: 2.7.0 transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@11.2.0: dependencies: acorn: 8.18.0 acorn-jsx: 5.3.2(acorn@8.18.0) - eslint-visitor-keys: 3.4.3 - - esprima@4.0.1: {} + eslint-visitor-keys: 5.0.1 esquery@1.7.0: dependencies: @@ -5359,8 +4029,6 @@ snapshots: dependencies: estraverse: 5.3.0 - estraverse@4.3.0: {} - estraverse@5.3.0: {} estree-walker@2.0.2: {} @@ -5369,17 +4037,7 @@ snapshots: eventemitter2@6.4.7: {} - exec-sh@0.3.6: {} - - execa@1.0.0: - dependencies: - cross-spawn: 6.0.6 - get-stream: 4.1.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 + eventemitter3@5.0.4: {} execa@4.1.0: dependencies: @@ -5397,145 +4055,46 @@ snapshots: dependencies: pify: 2.3.0 - exit@0.1.2: {} - - expand-brackets@2.1.4(supports-color@8.1.1): - dependencies: - debug: 2.6.9(supports-color@8.1.1) - define-property: 0.2.5 - extend-shallow: 2.0.1 - posix-character-classes: 0.1.1 - regex-not: 1.0.2 - snapdragon: 0.8.2(supports-color@8.1.1) - to-regex: 3.0.2 - transitivePeerDependencies: - - supports-color - - expect@26.6.2: - dependencies: - '@jest/types': 26.6.2 - ansi-styles: 4.3.0 - jest-get-type: 26.3.0 - jest-matcher-utils: 26.6.2 - jest-message-util: 26.6.2 - jest-regex-util: 26.0.0 + exponential-backoff@3.1.3: {} - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - extend-shallow@3.0.2: - dependencies: - assign-symbols: 1.0.0 - is-extendable: 1.0.1 + exsolve@1.1.1: {} extend@3.0.2: {} - extglob@2.0.4(supports-color@8.1.1): - dependencies: - array-unique: 0.3.2 - define-property: 1.0.0 - expand-brackets: 2.1.4(supports-color@8.1.1) - extend-shallow: 2.0.1 - fragment-cache: 0.2.1 - regex-not: 1.0.2 - snapdragon: 0.8.2(supports-color@8.1.1) - to-regex: 3.0.2 - transitivePeerDependencies: - - supports-color - - extract-zip@2.0.1(supports-color@8.1.1): - dependencies: - debug: 4.4.3(supports-color@8.1.1) - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - extsprintf@1.3.0: {} fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fastq@1.20.3: - dependencies: - reusify: 1.1.0 - - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - - figures@3.2.0: - dependencies: - escape-string-regexp: 1.0.5 - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - fill-range@4.0.0: - dependencies: - extend-shallow: 2.0.1 - is-number: 3.0.0 - repeat-string: 1.6.1 - to-regex-range: 2.1.1 + fast-uri@3.1.9: {} - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@3.0.0: - dependencies: - locate-path: 3.0.0 + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 - find-up@4.1.0: + file-entry-cache@11.1.5: dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 + flat-cache: 6.1.23 find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: + flat-cache@6.1.23: dependencies: + cacheable: 2.5.0 flatted: 3.4.4 - keyv: 4.5.4 - rimraf: 3.0.2 + hookified: 1.15.1 flatted@3.4.4: {} - for-in@1.0.2: {} - forever-agent@0.6.1: {} - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - form-data@3.0.5: + form-data@4.0.6: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -5543,15 +4102,11 @@ snapshots: hasown: 2.0.4 mime-types: 2.1.35 - fragment-cache@0.2.1: - dependencies: - map-cache: 0.2.2 - - fs-extra@7.0.1: + fs-extra@11.3.6: dependencies: graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 + jsonfile: 6.2.1 + universalify: 2.0.1 fs-extra@9.1.0: dependencies: @@ -5560,34 +4115,18 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fs-minipass@2.1.0: + fs-minipass@3.0.3: dependencies: - minipass: 3.3.6 - optional: true - - fs.realpath@1.0.0: {} + minipass: 7.1.3 fsevents@2.3.3: optional: true function-bind@1.1.2: {} - gauge@3.0.2: - dependencies: - aproba: 2.1.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - optional: true - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} + get-east-asian-width@1.7.0: {} get-intrinsic@1.3.0: dependencies: @@ -5602,10 +4141,6 @@ snapshots: hasown: 2.0.4 math-intrinsics: 1.1.0 - get-package-type@0.1.0: {} - - get-port@5.1.1: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -5613,67 +4148,31 @@ snapshots: get-stdin@5.0.1: {} - get-stdin@6.0.0: {} - - get-stream@4.1.0: - dependencies: - pump: 3.0.4 - get-stream@5.2.0: dependencies: pump: 3.0.4 - get-value@2.0.6: {} - - getos@3.2.1: - dependencies: - async: 3.2.6 - getpass@0.1.7: dependencies: assert-plus: 1.0.0 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - glob@7.2.3: + glob-to-regexp@0.4.1: {} + + glob@13.0.6: dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 global-dirs@3.0.1: dependencies: ini: 2.0.0 - global-prefix@3.0.0: - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - globrex@0.1.2: {} + globals@17.12.0: {} glur@1.1.2: {} @@ -5681,17 +4180,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - - growly@1.3.0: - optional: true - - has-ansi@2.0.0: - dependencies: - ansi-regex: 2.1.1 - - has-flag@3.0.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -5700,679 +4188,163 @@ snapshots: dependencies: has-symbols: 1.1.0 - has-unicode@2.0.1: - optional: true - - has-value@0.3.1: + hasha@5.2.2: dependencies: - get-value: 2.0.6 - has-values: 0.1.4 - isobject: 2.1.0 - - has-value@1.0.0: - dependencies: - get-value: 2.0.6 - has-values: 1.0.0 - isobject: 3.0.1 - - has-values@0.1.4: {} + is-stream: 2.0.1 + type-fest: 0.8.1 - has-values@1.0.0: + hashery@1.5.1: dependencies: - is-number: 3.0.0 - kind-of: 4.0.0 + hookified: 1.15.1 hasown@2.0.4: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - heic2any@0.0.4: {} - hosted-git-info@2.8.9: {} + hermes-estree@0.25.1: {} - html-encoding-sniffer@2.0.1: + hermes-parser@0.25.1: dependencies: - whatwg-encoding: 1.0.5 + hermes-estree: 0.25.1 + + hookified@1.15.1: {} - html-escaper@2.0.2: {} + hookified@2.2.0: {} - http-proxy-agent@4.0.1(supports-color@8.1.1): + hosted-git-info@9.0.3: dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2(supports-color@8.1.1) + lru-cache: 11.5.3 + + http-cache-semantics@4.3.0: {} + + http-proxy-agent@7.0.2(supports-color@8.1.1): + dependencies: + agent-base: 7.1.4 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - http-signature@1.3.6: + http-signature@1.4.0: dependencies: assert-plus: 1.0.0 jsprim: 2.0.2 sshpk: 1.18.0 - https-proxy-agent@5.0.1(supports-color@8.1.1): + https-proxy-agent@7.0.6(supports-color@8.1.1): dependencies: - agent-base: 6.0.2(supports-color@8.1.1) + agent-base: 7.1.4 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color human-signals@1.1.1: {} - iconv-lite@0.4.24: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - import-lazy@4.0.0: {} - - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - imurmurhash@0.1.4: {} - - indent-string@4.0.0: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - ini@1.3.8: {} - - ini@2.0.0: {} - - interpret@1.4.0: {} - - is-accessor-descriptor@1.0.2: - dependencies: - hasown: 2.0.4 - - is-arrayish@0.2.1: {} - - is-buffer@1.1.6: {} - - is-ci@2.0.0: - dependencies: - ci-info: 2.0.0 - - is-ci@3.0.1: - dependencies: - ci-info: 3.9.0 - - is-core-module@2.17.0: - dependencies: - hasown: 2.0.4 - - is-data-descriptor@1.0.1: - dependencies: - hasown: 2.0.4 - - is-descriptor@0.1.8: - dependencies: - is-accessor-descriptor: 1.0.2 - is-data-descriptor: 1.0.1 - - is-descriptor@1.0.4: - dependencies: - is-accessor-descriptor: 1.0.2 - is-data-descriptor: 1.0.1 - - is-docker@2.2.1: - optional: true - - is-extendable@0.1.1: {} - - is-extendable@1.0.1: - dependencies: - is-plain-object: 2.0.4 - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-generator-fn@2.1.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-installed-globally@0.4.0: - dependencies: - global-dirs: 3.0.1 - is-path-inside: 3.0.3 - - is-number@3.0.0: - dependencies: - kind-of: 3.2.2 - - is-number@7.0.0: {} - - is-path-inside@3.0.3: {} - - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - - is-potential-custom-element-name@1.0.1: {} - - is-stream@1.1.0: {} - - is-stream@2.0.1: {} - - is-typedarray@1.0.0: {} - - is-unicode-supported@0.1.0: {} - - is-windows@1.0.2: {} - - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 optional: true - isarray@1.0.0: {} - - isexe@2.0.0: {} - - isobject@2.1.0: - dependencies: - isarray: 1.0.0 - - isobject@3.0.1: {} - - isstream@0.1.2: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@4.0.3(supports-color@8.1.1): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@istanbuljs/schema': 0.1.6 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@5.2.1(supports-color@8.1.1): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/parser': 7.29.9 - '@istanbuljs/schema': 0.1.6 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@4.0.1(supports-color@8.1.1): - dependencies: - debug: 4.4.3(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - iterm2-version@4.2.0: - dependencies: - app-path: 3.3.0 - plist: 3.1.1 - - jest-changed-files@26.6.2: - dependencies: - '@jest/types': 26.6.2 - execa: 4.1.0 - throat: 5.0.0 - - jest-cli@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - '@jest/core': 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - '@jest/test-result': 26.6.2 - '@jest/types': 26.6.2 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.2.0 - is-ci: 2.0.0 - jest-config: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - jest-util: 26.6.2 - jest-validate: 26.6.2 - prompts: 2.4.2 - yargs: 15.4.1 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate - - jest-config@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@jest/test-sequencer': 26.6.3(supports-color@8.1.1) - '@jest/types': 26.6.2 - babel-jest: 26.6.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) - chalk: 4.1.2 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-environment-jsdom: 26.6.2(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - jest-environment-node: 26.6.2 - jest-get-type: 26.3.0 - jest-jasmine2: 26.6.3(supports-color@8.1.1) - jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 - jest-util: 26.6.2 - jest-validate: 26.6.2 - micromatch: 4.0.8 - pretty-format: 26.6.2 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate - - jest-diff@26.6.2: - dependencies: - chalk: 4.1.2 - diff-sequences: 26.6.2 - jest-get-type: 26.3.0 - pretty-format: 26.6.2 - - jest-diff@27.5.1: - dependencies: - chalk: 4.1.2 - diff-sequences: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - - jest-docblock@26.0.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@26.6.2: - dependencies: - '@jest/types': 26.6.2 - chalk: 4.1.2 - jest-get-type: 26.3.0 - jest-util: 26.6.2 - pretty-format: 26.6.2 - - jest-environment-jsdom@26.6.2(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - '@jest/environment': 26.6.2 - '@jest/fake-timers': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - jest-mock: 26.6.2 - jest-util: 26.6.2 - jsdom: 16.7.0(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate - - jest-environment-node@26.6.2: - dependencies: - '@jest/environment': 26.6.2 - '@jest/fake-timers': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - jest-mock: 26.6.2 - jest-util: 26.6.2 - - jest-get-type@26.3.0: {} - - jest-get-type@27.5.1: {} - - jest-haste-map@26.6.2(supports-color@8.1.1): - dependencies: - '@jest/types': 26.6.2 - '@types/graceful-fs': 4.1.9 - '@types/node': 20.19.43 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 26.0.0 - jest-serializer: 26.6.2 - jest-util: 26.6.2 - jest-worker: 26.6.2 - micromatch: 4.0.8 - sane: 4.1.0(supports-color@8.1.1) - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - transitivePeerDependencies: - - supports-color - - jest-image-snapshot@4.2.0(jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1)): - dependencies: - chalk: 1.1.3 - get-stdin: 5.0.1 - glur: 1.1.2 - jest: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - lodash: 4.18.1 - mkdirp: 0.5.6 - pixelmatch: 5.3.0 - pngjs: 3.4.0 - rimraf: 2.7.1 - ssim.js: 3.5.0 - - jest-jasmine2@26.6.3(supports-color@8.1.1): - dependencies: - '@babel/traverse': 7.29.8(supports-color@8.1.1) - '@jest/environment': 26.6.2 - '@jest/source-map': 26.6.2 - '@jest/test-result': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - chalk: 4.1.2 - co: 4.6.0 - expect: 26.6.2 - is-generator-fn: 2.1.0 - jest-each: 26.6.2 - jest-matcher-utils: 26.6.2 - jest-message-util: 26.6.2 - jest-runtime: 26.6.3(supports-color@8.1.1) - jest-snapshot: 26.6.2(supports-color@8.1.1) - jest-util: 26.6.2 - pretty-format: 26.6.2 - throat: 5.0.0 - transitivePeerDependencies: - - supports-color + ieee754@1.2.1: {} - jest-leak-detector@26.6.2: + ignore-walk@8.0.0: dependencies: - jest-get-type: 26.3.0 - pretty-format: 26.6.2 + minimatch: 10.2.6 - jest-matcher-utils@26.6.2: - dependencies: - chalk: 4.1.2 - jest-diff: 26.6.2 - jest-get-type: 26.3.0 - pretty-format: 26.6.2 + ignore@5.3.2: {} - jest-matcher-utils@27.5.1: - dependencies: - chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 + ignore@7.0.10: {} - jest-message-util@26.6.2: - dependencies: - '@babel/code-frame': 7.29.7 - '@jest/types': 26.6.2 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 26.6.2 - slash: 3.0.0 - stack-utils: 2.0.6 + import-lazy@4.0.0: {} - jest-mock@26.6.2: - dependencies: - '@jest/types': 26.6.2 - '@types/node': 20.19.43 + imurmurhash@0.1.4: {} - jest-pnp-resolver@1.2.3(jest-resolve@26.6.2): - optionalDependencies: - jest-resolve: 26.6.2 + ini@2.0.0: {} - jest-regex-util@26.0.0: {} + ini@6.0.0: {} - jest-resolve-dependencies@26.6.3(supports-color@8.1.1): - dependencies: - '@jest/types': 26.6.2 - jest-regex-util: 26.0.0 - jest-snapshot: 26.6.2(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color + ip-address@10.7.5: {} - jest-resolve@26.6.2: + is-core-module@2.17.0: dependencies: - '@jest/types': 26.6.2 - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-pnp-resolver: 1.2.3(jest-resolve@26.6.2) - jest-util: 26.6.2 - read-pkg-up: 7.0.1 - resolve: 1.22.12 - slash: 3.0.0 + hasown: 2.0.4 - jest-runner@26.6.3(supports-color@8.1.1): - dependencies: - '@jest/console': 26.6.2 - '@jest/environment': 26.6.2 - '@jest/test-result': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - chalk: 4.1.2 - emittery: 0.7.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - jest-docblock: 26.0.0 - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-leak-detector: 26.6.2 - jest-message-util: 26.6.2 - jest-resolve: 26.6.2 - jest-runtime: 26.6.3(supports-color@8.1.1) - jest-util: 26.6.2 - jest-worker: 26.6.2 - source-map-support: 0.5.21 - throat: 5.0.0 - transitivePeerDependencies: - - supports-color + is-extglob@2.1.1: {} - jest-runtime@26.6.3(supports-color@8.1.1): - dependencies: - '@jest/console': 26.6.2 - '@jest/environment': 26.6.2 - '@jest/fake-timers': 26.6.2 - '@jest/globals': 26.6.2 - '@jest/source-map': 26.6.2 - '@jest/test-result': 26.6.2 - '@jest/transform': 26.6.2(supports-color@8.1.1) - '@jest/types': 26.6.2 - '@types/yargs': 15.0.20 - chalk: 4.1.2 - cjs-module-lexer: 0.6.0 - collect-v8-coverage: 1.0.3 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-config: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-message-util: 26.6.2 - jest-mock: 26.6.2 - jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 - jest-snapshot: 26.6.2(supports-color@8.1.1) - jest-util: 26.6.2 - jest-validate: 26.6.2 - slash: 3.0.0 - strip-bom: 4.0.0 - yargs: 15.4.1 - transitivePeerDependencies: - - supports-color + is-fullwidth-code-point@3.0.0: {} - jest-serializer@26.6.2: + is-fullwidth-code-point@5.1.0: dependencies: - '@types/node': 20.19.43 - graceful-fs: 4.2.11 + get-east-asian-width: 1.7.0 - jest-snapshot@26.6.2(supports-color@8.1.1): + is-glob@4.0.3: dependencies: - '@babel/types': 7.29.8 - '@jest/types': 26.6.2 - '@types/babel__traverse': 7.28.0 - '@types/prettier': 2.7.3 - chalk: 4.1.2 - expect: 26.6.2 - graceful-fs: 4.2.11 - jest-diff: 26.6.2 - jest-get-type: 26.3.0 - jest-haste-map: 26.6.2(supports-color@8.1.1) - jest-matcher-utils: 26.6.2 - jest-message-util: 26.6.2 - jest-resolve: 26.6.2 - natural-compare: 1.4.0 - pretty-format: 26.6.2 - semver: 7.8.5 - transitivePeerDependencies: - - supports-color + is-extglob: 2.1.1 - jest-util@26.6.2: + is-installed-globally@0.4.0: dependencies: - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - chalk: 4.1.2 - graceful-fs: 4.2.11 - is-ci: 2.0.0 - micromatch: 4.0.8 + global-dirs: 3.0.1 + is-path-inside: 3.0.3 - jest-validate@26.6.2: - dependencies: - '@jest/types': 26.6.2 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 26.3.0 - leven: 3.1.0 - pretty-format: 26.6.2 + is-path-inside@3.0.3: {} - jest-watcher@26.6.2: - dependencies: - '@jest/test-result': 26.6.2 - '@jest/types': 26.6.2 - '@types/node': 20.19.43 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - jest-util: 26.6.2 - string-length: 4.0.2 + is-stream@2.0.1: {} - jest-worker@26.6.2: - dependencies: - '@types/node': 20.19.43 - merge-stream: 2.0.0 - supports-color: 7.2.0 + is-typedarray@1.0.0: {} - jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - '@jest/core': 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - import-local: 3.2.0 - jest-cli: 26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1) - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate + is-unicode-supported@0.1.0: {} - jju@1.4.0: {} + isexe@2.0.0: {} - js-tokens@4.0.0: {} + isexe@4.0.0: {} - js-yaml@3.15.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + isstream@0.1.2: {} - js-yaml@4.3.2: + jest-image-snapshot@6.5.2: dependencies: - argparse: 2.0.1 + chalk: 4.1.2 + get-stdin: 5.0.1 + glur: 1.1.2 + lodash: 4.18.1 + pixelmatch: 5.3.0 + pngjs: 3.4.0 + ssim.js: 3.5.0 - jsbn@0.1.1: {} + jiti@2.7.0: {} - jsdom@16.7.0(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): - dependencies: - abab: 2.0.6 - acorn: 8.18.0 - acorn-globals: 6.0.0 - cssom: 0.4.4 - cssstyle: 2.3.0 - data-urls: 2.0.0 - decimal.js: 10.6.0 - domexception: 2.0.1 - escodegen: 2.1.0 - form-data: 3.0.5 - html-encoding-sniffer: 2.0.1 - http-proxy-agent: 4.0.1(supports-color@8.1.1) - https-proxy-agent: 5.0.1(supports-color@8.1.1) - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.28 - parse5: 6.0.1 - saxes: 5.0.1 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-hr-time: 1.0.2 - w3c-xmlserializer: 2.0.0 - webidl-conversions: 6.1.0 - whatwg-encoding: 1.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - ws: 7.5.13 - xml-name-validator: 3.0.0 - optionalDependencies: - canvas: 2.11.2(supports-color@8.1.1) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + jju@1.4.0: {} - jsesc@3.1.0: {} + js-tokens@4.0.0: {} + + jsbn@0.1.1: {} - json-buffer@3.0.1: {} + jsesc@3.1.0: {} - json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@5.0.0: {} json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-nice@1.1.4: {} + json-stringify-safe@5.0.1: {} json5@2.2.3: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + jsprim@2.0.2: dependencies: assert-plus: 1.0.0 @@ -6380,73 +4352,112 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 - keyv@4.5.4: + just-diff-apply@5.5.0: {} + + just-diff@6.0.2: {} + + keyv@5.6.0: dependencies: - json-buffer: 3.0.1 + '@keyv/serialize': 1.1.1 + + kolorist@1.8.0: {} + + konva@10.7.0: {} - kind-of@3.2.2: + levn@0.4.1: dependencies: - is-buffer: 1.1.6 + prelude-ls: 1.2.1 + type-check: 0.4.0 - kind-of@4.0.0: + license-checker-rseidelsohn@5.0.1(supports-color@8.1.1): dependencies: - is-buffer: 1.1.6 + '@npmcli/arborist': 9.6.0(supports-color@8.1.1) + '@npmcli/package-json': 7.0.5 + chalk: 4.1.2 + debug: 4.4.3(supports-color@8.1.1) + lodash.clonedeep: 4.5.0 + mkdirp: 1.0.4 + nopt: 7.2.1 + semver: 7.8.5 + spdx-correct: 3.2.0 + spdx-expression-parse: 4.0.0 + spdx-satisfies: 6.0.0 + treeify: 1.1.0 + transitivePeerDependencies: + - supports-color + + lightningcss-android-arm64@1.33.0: + optional: true - kind-of@6.0.3: {} + lightningcss-darwin-arm64@1.33.0: + optional: true - kleur@3.0.3: {} + lightningcss-darwin-x64@1.33.0: + optional: true - kolorist@1.8.0: {} + lightningcss-freebsd-x64@1.33.0: + optional: true - konva@8.4.3: {} + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true - lazy-ass@1.6.0: {} + lightningcss-linux-arm64-gnu@1.33.0: + optional: true - leven@3.1.0: {} + lightningcss-linux-arm64-musl@1.33.0: + optional: true - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 + lightningcss-linux-x64-gnu@1.33.0: + optional: true - lines-and-columns@1.2.4: {} + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true - listr2@3.14.0(enquirer@2.4.1): + lightningcss@1.33.0: dependencies: - cli-truncate: 2.1.0 + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 colorette: 2.0.20 - log-update: 4.0.0 - p-map: 4.0.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 rfdc: 1.4.1 - rxjs: 7.8.2 - through: 2.3.8 - wrap-ansi: 7.0.0 - optionalDependencies: - enquirer: 2.4.1 + wrap-ansi: 9.0.2 - locate-path@3.0.0: + local-pkg@1.2.1: dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 + mlly: 1.8.2 + pkg-types: 2.3.3 + quansync: 0.2.11 locate-path@6.0.0: dependencies: p-locate: 5.0.0 - lodash.get@4.4.2: {} - - lodash.isequal@4.5.0: {} - - lodash.merge@4.6.2: {} + lodash.clonedeep@4.5.0: {} lodash.once@4.1.1: {} - lodash@4.17.23: {} - lodash@4.18.1: {} log-symbols@4.1.0: @@ -6454,73 +4465,45 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - log-update@4.0.0: + log-update@6.1.0: dependencies: - ansi-escapes: 4.3.2 - cli-cursor: 3.1.0 - slice-ansi: 4.0.0 - wrap-ansi: 6.2.0 + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + lru-cache@11.5.3: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 - make-dir@3.1.0: - dependencies: - semver: 6.3.1 - optional: true - - make-dir@4.0.0: - dependencies: - semver: 7.8.5 - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - map-cache@0.2.2: {} - - map-visit@1.0.0: - dependencies: - object-visit: 1.0.1 + make-fetch-happen@15.0.6(supports-color@8.1.1): + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/agent': 4.0.2(supports-color@8.1.1) + '@npmcli/redact': 4.0.0 + cacache: 20.0.4 + http-cache-semantics: 4.3.0 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + negotiator: 1.1.0 + proc-log: 6.1.0 + ssri: 13.0.1 + transitivePeerDependencies: + - supports-color math-intrinsics@1.1.0: {} merge-stream@2.0.0: {} - merge2@1.4.1: {} - - micromatch@3.1.10(supports-color@8.1.1): - dependencies: - arr-diff: 4.0.0 - array-unique: 0.3.2 - braces: 2.3.2(supports-color@8.1.1) - define-property: 2.0.2 - extend-shallow: 3.0.2 - extglob: 2.0.4(supports-color@8.1.1) - fragment-cache: 0.2.1 - kind-of: 6.0.3 - nanomatch: 1.2.13(supports-color@8.1.1) - object.pick: 1.3.0 - regex-not: 1.0.2 - snapdragon: 0.8.2(supports-color@8.1.1) - to-regex: 3.0.2 - transitivePeerDependencies: - - supports-color - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - mime-db@1.52.0: {} mime-types@2.1.35: @@ -6529,155 +4512,141 @@ snapshots: mimic-fn@2.1.0: {} - mimic-response@2.1.0: - optional: true - - minimatch@3.0.8: - dependencies: - brace-expansion: 1.1.21 + mimic-function@5.0.1: {} - minimatch@3.1.5: + minimatch@10.2.3: dependencies: - brace-expansion: 1.1.21 + brace-expansion: 5.0.12 - minimatch@9.0.9: + minimatch@10.2.6: dependencies: - brace-expansion: 2.1.7 + brace-expansion: 5.0.12 minimist@1.2.8: {} - minipass@3.3.6: + minipass-collect@2.0.1: dependencies: - yallist: 4.0.0 - optional: true + minipass: 7.1.3 - minipass@5.0.0: - optional: true + minipass-fetch@5.0.2: + dependencies: + minipass: 7.1.3 + minipass-sized: 2.0.0 + minizlib: 3.1.0 + optionalDependencies: + iconv-lite: 0.7.3 - minizlib@2.1.2: + minipass-flush@1.0.7: dependencies: minipass: 3.3.6 - yallist: 4.0.0 - optional: true - mixin-deep@1.3.2: + minipass-pipeline@1.2.4: dependencies: - for-in: 1.0.2 - is-extendable: 1.0.1 + minipass: 3.3.6 - mkdirp@0.5.6: + minipass-sized@2.0.0: dependencies: - minimist: 1.2.8 + minipass: 7.1.3 - mkdirp@1.0.4: - optional: true + minipass@3.3.6: + dependencies: + yallist: 4.0.0 - ms@2.0.0: {} + minipass@7.1.3: {} - ms@2.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 - muggle-string@0.3.1: {} + mkdirp@1.0.4: {} - nan@2.29.0: - optional: true + mlly@1.8.2: + dependencies: + acorn: 8.18.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 - nanoid@3.3.19: {} - - nanomatch@1.2.13(supports-color@8.1.1): - dependencies: - arr-diff: 4.0.0 - array-unique: 0.3.2 - define-property: 2.0.2 - extend-shallow: 3.0.2 - fragment-cache: 0.2.1 - is-windows: 1.0.2 - kind-of: 6.0.3 - object.pick: 1.3.0 - regex-not: 1.0.2 - snapdragon: 0.8.2(supports-color@8.1.1) - to-regex: 3.0.2 - transitivePeerDependencies: - - supports-color + ms@2.1.3: {} - natural-compare-lite@1.4.1: {} + nanoid@3.3.20: {} natural-compare@1.4.0: {} - nice-try@1.0.5: {} - - node-fetch@2.7.0: + negotiator@1.1.0: dependencies: - whatwg-url: 5.0.0 - optional: true - - node-int64@0.4.0: {} + content-type: 2.1.0 - node-notifier@8.0.2: + node-gyp@12.4.0: dependencies: - growly: 1.3.0 - is-wsl: 2.2.0 + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + nopt: 9.0.0 + proc-log: 6.1.0 semver: 7.8.5 - shellwords: 0.1.1 - uuid: 8.3.2 - which: 2.0.2 - optional: true + tar: 7.5.22 + tinyglobby: 0.2.17 + undici: 6.29.1 + which: 6.0.1 node-releases@2.0.57: {} - nopt@5.0.0: + nopt@7.2.1: dependencies: - abbrev: 1.1.1 - optional: true + abbrev: 2.0.0 - normalize-package-data@2.5.0: + nopt@9.0.0: dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.12 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 + abbrev: 4.0.0 - normalize-path@2.1.1: + npm-bundled@5.0.0: dependencies: - remove-trailing-separator: 1.1.0 - - normalize-path@3.0.0: {} + npm-normalize-package-bin: 5.0.0 - npm-run-path@2.0.2: + npm-install-checks@8.0.0: dependencies: - path-key: 2.0.1 + semver: 7.8.5 - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 + npm-normalize-package-bin@5.0.0: {} - npmlog@5.0.1: + npm-package-arg@13.0.2: dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - optional: true - - nwsapi@2.2.28: {} - - object-assign@4.1.1: - optional: true + hosted-git-info: 9.0.3 + proc-log: 6.1.0 + semver: 7.8.5 + validate-npm-package-name: 7.0.2 - object-copy@0.1.0: + npm-packlist@10.0.4: dependencies: - copy-descriptor: 0.1.1 - define-property: 0.2.5 - kind-of: 3.2.2 + ignore-walk: 8.0.0 + proc-log: 6.1.0 - object-inspect@1.13.4: {} + npm-pick-manifest@11.0.3: + dependencies: + npm-install-checks: 8.0.0 + npm-normalize-package-bin: 5.0.0 + npm-package-arg: 13.0.2 + semver: 7.8.5 - object-visit@1.0.1: + npm-registry-fetch@19.1.1(supports-color@8.1.1): dependencies: - isobject: 3.0.1 + '@npmcli/redact': 4.0.0 + jsonparse: 1.3.1 + make-fetch-happen: 15.0.6(supports-color@8.1.1) + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minizlib: 3.1.0 + npm-package-arg: 13.0.2 + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color - object.pick@1.3.0: + npm-run-path@4.0.1: dependencies: - isobject: 3.0.1 + path-key: 3.1.1 + + object-inspect@1.13.4: {} once@1.4.0: dependencies: @@ -6687,6 +4656,10 @@ snapshots: dependencies: mimic-fn: 2.1.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -6698,79 +4671,64 @@ snapshots: ospath@1.2.2: {} - p-each-series@2.2.0: {} - - p-finally@1.0.0: {} - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@3.0.0: - dependencies: - p-limit: 2.3.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-map@4.0.0: - dependencies: - aggregate-error: 3.1.0 - - p-try@2.2.0: {} + p-map@7.0.8: {} + + pacote@21.5.1(supports-color@8.1.1): + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/git': 7.0.2 + '@npmcli/installed-package-contents': 4.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/promise-spawn': 9.0.1 + '@npmcli/run-script': 10.0.4 + cacache: 20.0.4 + fs-minipass: 3.0.3 + minipass: 7.1.3 + npm-package-arg: 13.0.2 + npm-packlist: 10.0.4 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1(supports-color@8.1.1) + proc-log: 6.1.0 + sigstore: 4.1.1(supports-color@8.1.1) + ssri: 13.0.1 + tar: 7.5.22 + transitivePeerDependencies: + - supports-color pako@1.0.11: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: + parse-conflict-json@5.0.1: dependencies: - '@babel/code-frame': 7.29.7 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse5@6.0.1: {} - - pascalcase@0.1.1: {} + json-parse-even-better-errors: 5.0.0 + just-diff: 6.0.2 + just-diff-apply: 5.5.0 path-browserify@1.0.1: {} - path-exists@3.0.0: {} - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - - path-key@2.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} - path-type@4.0.0: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 - path2d-polyfill@2.0.1: - optional: true + pathe@2.0.3: {} - pdfjs-dist@3.11.174(supports-color@8.1.1): + pdfjs-dist@6.3.289: optionalDependencies: - canvas: 2.11.2(supports-color@8.1.1) - path2d-polyfill: 2.0.1 - transitivePeerDependencies: - - encoding - - supports-color + '@napi-rs/canvas': 1.0.9 pend@1.2.0: {} @@ -6778,79 +4736,58 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.7: {} pify@2.3.0: {} - pirates@4.0.7: {} - pixelmatch@5.3.0: dependencies: pngjs: 6.0.0 - pkg-dir@3.0.0: + pkg-types@1.3.1: dependencies: - find-up: 3.0.0 + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 - pkg-dir@4.2.0: + pkg-types@2.3.3: dependencies: - find-up: 4.1.0 - - plist@3.1.1: - dependencies: - '@xmldom/xmldom': 0.9.12 - base64-js: 1.5.1 - xmlbuilder: 15.1.1 + confbox: 0.3.1 + exsolve: 1.1.1 + pathe: 2.0.3 pngjs@3.4.0: {} pngjs@6.0.0: {} - posix-character-classes@0.1.1: {} + postcss-selector-parser@7.1.6: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 - postcss@8.5.28: + postcss@8.5.31: dependencies: - nanoid: 3.3.19 + nanoid: 3.3.20 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - - prettier@2.8.8: {} - - pretty-bytes@5.6.0: {} + prettier@3.9.9: {} - pretty-format@26.6.2: - dependencies: - '@jest/types': 26.6.2 - ansi-regex: 5.0.1 - ansi-styles: 4.3.0 - react-is: 17.0.2 + pretty-bytes@5.6.0: {} - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 + proc-log@6.1.0: {} process@0.11.10: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 + proggy@4.0.0: {} - proxy-from-env@1.0.0: {} + promise-all-reject-late@1.0.1: {} - psl@1.15.0: - dependencies: - punycode: 2.3.1 + promise-call-limit@3.0.2: {} + + proxy-from-env@1.0.0: {} pump@3.0.4: dependencies: @@ -6859,84 +4796,31 @@ snapshots: punycode@2.3.1: {} - qs@6.10.7: + qified@0.10.1: dependencies: - side-channel: 1.1.1 + hookified: 2.2.0 - querystringify@2.2.0: {} + qs@6.16.0: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 - queue-microtask@1.2.3: {} + quansync@0.2.11: {} react-dom@19.3.0(react@19.3.0): dependencies: react: 19.3.0 scheduler: 0.28.0 - react-is@17.0.2: {} - - react-refresh@0.17.0: {} - react@19.3.0: {} - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - optional: true - - rechoir@0.6.2: - dependencies: - resolve: 1.22.12 - - regex-not@1.0.2: - dependencies: - extend-shallow: 3.0.2 - safe-regex: 1.1.0 - - remove-trailing-separator@1.1.0: {} - - repeat-element@1.1.4: {} - - repeat-string@1.6.1: {} + read-cmd-shim@6.0.0: {} request-progress@3.0.0: dependencies: throttleit: 1.0.1 - require-directory@2.1.1: {} - - require-main-filename@2.0.0: {} - - requires-port@1.0.0: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - - resolve-from@4.0.0: {} - - resolve-from@5.0.0: {} - - resolve-url@0.2.1: {} - - resolve@1.19.0: - dependencies: - is-core-module: 2.17.0 - path-parse: 1.0.7 + require-from-string@2.0.2: {} resolve@1.22.12: dependencies: @@ -6945,107 +4829,79 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@3.1.0: + restore-cursor@5.1.0: dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - ret@0.1.15: {} - - reusify@1.1.0: {} + onetime: 7.0.0 + signal-exit: 4.1.0 rfdc@1.4.1: {} - rimraf@2.7.1: + rolldown@1.2.11: dependencies: - glob: 7.2.3 + '@oxc-project/types': 0.151.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.11 + '@rolldown/binding-android-arm64': 1.2.11 + '@rolldown/binding-darwin-arm64': 1.2.11 + '@rolldown/binding-darwin-x64': 1.2.11 + '@rolldown/binding-freebsd-x64': 1.2.11 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.11 + '@rolldown/binding-linux-arm64-gnu': 1.2.11 + '@rolldown/binding-linux-arm64-musl': 1.2.11 + '@rolldown/binding-linux-ppc64-gnu': 1.2.11 + '@rolldown/binding-linux-s390x-gnu': 1.2.11 + '@rolldown/binding-linux-x64-gnu': 1.2.11 + '@rolldown/binding-linux-x64-musl': 1.2.11 + '@rolldown/binding-openharmony-arm64': 1.2.11 + '@rolldown/binding-win32-arm64-msvc': 1.2.11 + '@rolldown/binding-win32-x64-msvc': 1.2.11 + optional: true - rimraf@3.0.2: + rolldown@1.2.15: dependencies: - glob: 7.2.3 + '@oxc-project/types': 0.155.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.15 + '@rolldown/binding-android-arm64': 1.2.15 + '@rolldown/binding-darwin-arm64': 1.2.15 + '@rolldown/binding-darwin-x64': 1.2.15 + '@rolldown/binding-freebsd-x64': 1.2.15 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.15 + '@rolldown/binding-linux-arm64-gnu': 1.2.15 + '@rolldown/binding-linux-arm64-musl': 1.2.15 + '@rolldown/binding-linux-ppc64-gnu': 1.2.15 + '@rolldown/binding-linux-s390x-gnu': 1.2.15 + '@rolldown/binding-linux-x64-gnu': 1.2.15 + '@rolldown/binding-linux-x64-musl': 1.2.15 + '@rolldown/binding-openharmony-arm64': 1.2.15 + '@rolldown/binding-win32-arm64-msvc': 1.2.15 + '@rolldown/binding-win32-x64-msvc': 1.2.15 rollup@3.30.0: optionalDependencies: fsevents: 2.3.3 - - rsvp@4.8.5: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 + optional: true safe-buffer@5.2.1: {} - safe-regex@1.1.0: - dependencies: - ret: 0.1.15 - safer-buffer@2.1.2: {} - sane@4.1.0(supports-color@8.1.1): - dependencies: - '@cnakazawa/watch': 1.0.4 - anymatch: 2.0.0(supports-color@8.1.1) - capture-exit: 2.0.0 - exec-sh: 0.3.6 - execa: 1.0.0 - fb-watchman: 2.0.2 - micromatch: 3.1.10(supports-color@8.1.1) - minimist: 1.2.8 - walker: 1.0.8 - transitivePeerDependencies: - - supports-color - - saxes@5.0.1: - dependencies: - xmlchars: 2.2.0 - scheduler@0.28.0: {} - semver@5.7.2: {} - semver@6.3.1: {} - semver@7.5.4: - dependencies: - lru-cache: 6.0.0 + semver@7.7.4: {} semver@7.8.5: {} - set-blocking@2.0.0: {} - - set-value@2.0.1: - dependencies: - extend-shallow: 2.0.1 - is-extendable: 0.1.1 - is-plain-object: 2.0.4 - split-string: 3.1.0 - - shebang-command@1.2.0: - dependencies: - shebang-regex: 1.0.0 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} - shelljs@0.8.5: - dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - - shellwords@0.1.1: - optional: true - side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -7076,77 +4932,53 @@ snapshots: signal-exit@3.0.7: {} - simple-concat@1.0.1: - optional: true - - simple-get@3.1.1: - dependencies: - decompress-response: 4.2.1 - once: 1.4.0 - simple-concat: 1.0.1 - optional: true - - sisteransi@1.0.5: {} + signal-exit@4.1.0: {} - slash@3.0.0: {} - - slice-ansi@3.0.0: + sigstore@4.1.1(supports-color@8.1.1): dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.2 + '@sigstore/sign': 4.1.1(supports-color@8.1.1) + '@sigstore/tuf': 4.0.2(supports-color@8.1.1) + '@sigstore/verify': 3.1.1 + transitivePeerDependencies: + - supports-color - slice-ansi@4.0.0: + slice-ansi@7.1.2: dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 - snapdragon-node@2.1.1: + slice-ansi@8.0.0: dependencies: - define-property: 1.0.0 - isobject: 3.0.1 - snapdragon-util: 3.0.1 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 - snapdragon-util@3.0.1: - dependencies: - kind-of: 3.2.2 + smart-buffer@4.2.0: {} - snapdragon@0.8.2(supports-color@8.1.1): + socks-proxy-agent@8.0.5(supports-color@8.1.1): dependencies: - base: 0.11.2 - debug: 2.6.9(supports-color@8.1.1) - define-property: 0.2.5 - extend-shallow: 2.0.1 - map-cache: 0.2.2 - source-map: 0.5.7 - source-map-resolve: 0.5.3 - use: 3.1.1 + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + socks: 2.8.10 transitivePeerDependencies: - supports-color - source-map-js@1.2.1: {} - - source-map-resolve@0.5.3: + socks@2.8.10: dependencies: - atob: 2.1.2 - decode-uri-component: 0.2.2 - resolve-url: 0.2.1 - source-map-url: 0.4.1 - urix: 0.1.0 - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map-url@0.4.1: {} + ip-address: 10.7.5 + smart-buffer: 4.2.0 - source-map@0.5.7: {} + source-map-js@1.2.2: {} source-map@0.6.1: {} - source-map@0.7.6: {} + spdx-compare@1.0.0: + dependencies: + array-find-index: 1.0.2 + spdx-expression-parse: 3.0.1 + spdx-ranges: 2.1.1 spdx-correct@3.2.0: dependencies: @@ -7160,11 +4992,20 @@ snapshots: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.24 + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.24 + spdx-license-ids@3.0.24: {} - split-string@3.1.0: + spdx-ranges@2.1.1: {} + + spdx-satisfies@6.0.0: dependencies: - extend-shallow: 3.0.2 + spdx-compare: 1.0.0 + spdx-expression-parse: 3.0.1 + spdx-ranges: 2.1.1 sprintf-js@1.0.3: {} @@ -7182,55 +5023,39 @@ snapshots: ssim.js@3.5.0: {} - stack-utils@2.0.6: + ssri@13.0.1: dependencies: - escape-string-regexp: 2.0.0 - - static-extend@0.1.2: - dependencies: - define-property: 0.2.5 - object-copy: 0.1.0 + minipass: 7.1.3 string-argv@0.3.2: {} - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string_decoder@1.3.0: + string-width@7.2.0: dependencies: - safe-buffer: 5.2.1 - optional: true + emoji-regex: 10.6.0 + get-east-asian-width: 1.7.0 + strip-ansi: 7.2.0 - strip-ansi@3.0.1: + string-width@8.3.0: dependencies: - ansi-regex: 2.1.1 + get-east-asian-width: 1.7.0 + strip-ansi: 7.2.0 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-bom@4.0.0: {} - - strip-eof@1.0.0: {} + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.4.0 strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} - - supports-color@2.0.0: {} - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7239,110 +5064,54 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-hyperlinks@2.3.0: - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - supports-preserve-symlinks-flag@1.0.0: {} - symbol-tree@3.2.4: {} - - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - optional: true - - term-img@4.1.0: - dependencies: - ansi-escapes: 4.3.2 - iterm2-version: 4.2.0 - - terminal-link@2.1.1: - dependencies: - ansi-escapes: 4.3.2 - supports-hyperlinks: 2.3.0 + systeminformation@5.33.14: {} - test-exclude@6.0.0: + tar@7.5.22: dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 7.2.3 - minimatch: 3.1.5 - - text-table@0.2.0: {} - - throat@5.0.0: {} + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 throttleit@1.0.1: {} - through@2.3.8: {} - - tmp@0.2.7: {} - - tmpl@1.0.5: {} - - to-object-path@0.3.0: + tinyglobby@0.2.17: dependencies: - kind-of: 3.2.2 - - to-regex-range@2.1.1: - dependencies: - is-number: 3.0.0 - repeat-string: 1.6.1 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 - to-regex@3.0.2: - dependencies: - define-property: 2.0.2 - extend-shallow: 3.0.2 - regex-not: 1.0.2 - safe-regex: 1.1.0 + tldts-core@6.1.86: {} - tough-cookie@4.1.4: + tldts@6.1.86: dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 + tldts-core: 6.1.86 - tr46@0.0.3: - optional: true + tmp@0.2.7: {} - tr46@2.1.0: + tough-cookie@5.1.2: dependencies: - punycode: 2.3.1 + tldts: 6.1.86 - ts-patch@1.4.5(typescript@4.9.5): - dependencies: - chalk: 4.1.2 - glob: 7.2.3 - global-prefix: 3.0.0 - minimist: 1.2.8 - resolve: 1.22.12 - shelljs: 0.8.5 - strip-ansi: 6.0.1 - typescript: 4.9.5 + tree-kill@1.2.2: {} - tsconfck@3.1.6(typescript@4.9.5): - optionalDependencies: - typescript: 4.9.5 + treeify@1.1.0: {} - tslib@1.14.1: {} + treeverse@3.0.0: {} - tslib@2.8.1: {} + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 - tsutils@3.21.0(typescript@4.9.5): + tuf-js@4.1.0(supports-color@8.1.1): dependencies: - tslib: 1.14.1 - typescript: 4.9.5 + '@tufjs/models': 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + make-fetch-happen: 15.0.6(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color tunnel-agent@0.6.0: dependencies: @@ -7354,48 +5123,57 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-detect@4.0.8: {} - - type-fest@0.20.2: {} - - type-fest@0.21.3: {} - - type-fest@0.6.0: {} - type-fest@0.8.1: {} - typedarray-to-buffer@3.1.5: + typescript-eslint@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3): dependencies: - is-typedarray: 1.0.0 - - typescript-transform-paths@3.5.6(typescript@4.9.5): - dependencies: - minimatch: 9.0.9 - typescript: 4.9.5 - - typescript@4.9.5: {} + '@typescript-eslint/eslint-plugin': 8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.71.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.71.0(eslint@10.11.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color - typescript@5.4.2: {} + typescript@5.9.3: {} - undici-types@6.21.0: {} + typescript@6.0.3: {} - union-value@1.0.1: - dependencies: - arr-union: 3.1.0 - get-value: 2.0.6 - is-extendable: 0.1.1 - set-value: 2.0.1 + ufo@1.6.4: {} - universalify@0.1.2: {} + undici-types@7.24.6: {} - universalify@0.2.0: {} + undici@6.29.1: {} universalify@2.0.1: {} - unset-value@1.0.0: + unplugin-dts@1.1.1(@microsoft/api-extractor@7.59.3(@types/node@24.19.0))(rolldown@1.2.11)(rollup@3.30.0)(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)): + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@3.30.0) + '@volar/typescript': 2.4.28(typescript@6.0.3) + compare-versions: 6.1.1 + debug: 4.4.3(supports-color@8.1.1) + glob-to-regexp: 0.4.1 + kolorist: 1.8.0 + local-pkg: 1.2.1 + magic-string: 0.30.21 + typescript: 6.0.3 + unplugin: 2.3.11 + optionalDependencies: + '@microsoft/api-extractor': 7.59.3(@types/node@24.19.0) + rolldown: 1.2.11 + rollup: 3.30.0 + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) + transitivePeerDependencies: + - supports-color + + unplugin@2.3.11: dependencies: - has-value: 0.3.1 - isobject: 3.0.1 + '@jridgewell/remapping': 2.3.5 + acorn: 8.18.0 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 untildify@4.0.0: {} @@ -7409,36 +5187,13 @@ snapshots: dependencies: punycode: 2.3.1 - urix@0.1.0: {} - - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - - use@3.1.1: {} - utif@3.1.0: dependencies: pako: 1.0.11 - util-deprecate@1.0.2: - optional: true - - uuid@8.3.2: {} - - v8-to-istanbul@7.1.2: - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 1.9.0 - source-map: 0.7.6 - - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 + util-deprecate@1.0.2: {} - validator@13.15.35: {} + validate-npm-package-name@7.0.2: {} verror@1.10.0: dependencies: @@ -7446,174 +5201,78 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-plugin-dts@3.9.1(@types/node@20.19.43)(rollup@3.30.0)(supports-color@8.1.1)(typescript@4.9.5)(vite@4.5.14(@types/node@20.19.43)): - dependencies: - '@microsoft/api-extractor': 7.43.0(@types/node@20.19.43) - '@rollup/pluginutils': 5.4.0(rollup@3.30.0) - '@vue/language-core': 1.8.27(typescript@4.9.5) - debug: 4.4.3(supports-color@8.1.1) - kolorist: 1.8.0 - magic-string: 0.30.21 - typescript: 4.9.5 - vue-tsc: 1.8.27(typescript@4.9.5) - optionalDependencies: - vite: 4.5.14(@types/node@20.19.43) - transitivePeerDependencies: - - '@types/node' - - rollup - - supports-color - - vite-tsconfig-paths@4.3.2(supports-color@8.1.1)(typescript@4.9.5)(vite@4.5.14(@types/node@20.19.43)): + vite-plugin-dts@5.1.1(@microsoft/api-extractor@7.59.3(@types/node@24.19.0))(rolldown@1.2.11)(rollup@3.30.0)(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)): dependencies: - debug: 4.4.3(supports-color@8.1.1) - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@4.9.5) + unplugin-dts: 1.1.1(@microsoft/api-extractor@7.59.3(@types/node@24.19.0))(rolldown@1.2.11)(rollup@3.30.0)(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)) optionalDependencies: - vite: 4.5.14(@types/node@20.19.43) + '@microsoft/api-extractor': 7.59.3(@types/node@24.19.0) + rollup: 3.30.0 + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) transitivePeerDependencies: + - '@rspack/core' + - '@vue/language-core' + - esbuild + - rolldown - supports-color - typescript + - webpack - vite@4.5.14(@types/node@20.19.43): + vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0): dependencies: - esbuild: 0.18.20 - postcss: 8.5.28 - rollup: 3.30.0 + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.31 + rolldown: 1.2.15 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.19.43 + '@types/node': 24.19.0 fsevents: 2.3.3 + jiti: 2.7.0 - vue-template-compiler@2.7.16: - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - vue-tsc@1.8.27(typescript@4.9.5): - dependencies: - '@volar/typescript': 1.11.1(typescript@4.9.5) - '@vue/language-core': 1.8.27(typescript@4.9.5) - semver: 7.8.5 - typescript: 4.9.5 - - w3c-hr-time@1.0.2: - dependencies: - browser-process-hrtime: 1.0.0 - - w3c-xmlserializer@2.0.0: - dependencies: - xml-name-validator: 3.0.0 - - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - - webidl-conversions@3.0.1: - optional: true - - webidl-conversions@5.0.0: {} - - webidl-conversions@6.1.0: {} - - whatwg-encoding@1.0.5: - dependencies: - iconv-lite: 0.4.24 - - whatwg-mimetype@2.3.0: {} + vscode-uri@3.2.0: {} - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - optional: true - - whatwg-url@8.7.0: - dependencies: - lodash: 4.18.1 - tr46: 2.1.0 - webidl-conversions: 6.1.0 - - which-module@2.0.1: {} + walk-up-path@4.0.0: {} - which@1.3.1: - dependencies: - isexe: 2.0.0 + webpack-virtual-modules@0.6.2: {} which@2.0.2: dependencies: isexe: 2.0.0 - wide-align@1.1.5: + which@6.0.1: dependencies: - string-width: 4.2.3 - optional: true + isexe: 4.0.0 word-wrap@1.2.5: {} - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 wrappy@1.0.2: {} - write-file-atomic@3.0.3: + write-file-atomic@7.0.1: dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 + signal-exit: 4.1.0 ws@7.5.13: {} - xml-name-validator@3.0.0: {} - - xmlbuilder@15.1.1: {} - - xmlchars@2.2.0: {} - - y18n@4.0.3: {} - yallist@3.1.1: {} yallist@4.0.0: {} - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 + yallist@5.0.0: {} - yauzl@2.10.0: + yauzl@3.4.0: dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 + pend: 1.2.0 yocto-queue@0.1.0: {} - z-schema@5.0.5: + zod-validation-error@4.0.2(zod@4.6.7): dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.15.35 - optionalDependencies: - commander: 9.5.0 + zod: 4.6.7 + + zod@4.6.7: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index be73870..669d914 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,15 @@ allowBuilds: + # optional dep of pdfjs-dist 3.x (Node only); not needed in the browser canvas: false cypress: true esbuild: true +overrides: + fast-uri@>=3.0.0 <3.1.9: ^3.1.9 + ip-address@<=10.7.3: ^10.7.4 + pdfjs-dist@<=4.1.392: ^4.2.67 + source-map-js@>=1.0.0 <1.2.2: ^1.2.2 + tar@<=7.5.20: ^7.5.21 + undici@>=6.5.0 <6.29.1: ^6.29.1 + uuid@<11.1.1: ^11.1.1 + vite@>=8.3.0 <=8.3.2: ^8.3.3 + zod@<4.6.6: ^4.6.6 diff --git a/prek.toml b/prek.toml new file mode 100644 index 0000000..690c5a1 --- /dev/null +++ b/prek.toml @@ -0,0 +1,41 @@ +# Paths ignored by eslint.config.ts / .prettierignore; keep in sync. +exclude = "^(docs|dist|node_modules|cypress/snapshots)/" + +[[repos]] +repo = "local" + +[[repos.hooks]] +id = "typecheck" +name = "Typecheck" +entry = "pnpm exec tsc --noEmit" +language = "system" +pass_filenames = false +types_or = ["ts", "tsx"] + +[[repos.hooks]] +id = "eslint-fix" +name = "ESLint Fix" +entry = "pnpm exec eslint --fix --max-warnings 0" +language = "system" +types_or = ["ts", "tsx"] + +[[repos.hooks]] +id = "prettier" +name = "Prettier" +entry = "pnpm exec prettier --write --ignore-unknown" +language = "system" + +[[repos.hooks]] +id = "license-check" +name = "License Check" +entry = "pnpm run license:check" +language = "system" +pass_filenames = false +stages = ["pre-push"] + +[[repos]] +repo = "https://github.com/betterleaks/betterleaks" +rev = "v1.9.0" +hooks = [ + { id = "betterleaks" } +] diff --git a/src/common/constants.ts b/src/common/constants.ts index 1418057..a1c1ab8 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,45 +1,46 @@ -import { Line } from 'konva/lib/shapes/Line' +import type { Line } from 'konva/lib/shapes/Line'; -import { AnnotationData } from '..' +import type { AnnotationData } from '@/common/types'; export const DEFAULT_STYLE = { height: 300, width: 300, display: 'flex', backgroundColor: '#001429', -} +}; -export enum KONVA_REFS { - shapesLayer = 'shapes-layer', - shape = 'shape', +export enum KonvaRefs { + ShapesLayer = 'shapes-layer', + Shape = 'shape', } -const STROKE_COLOR = '#FF0000' -const SELECTION_FILL_COLOR = 'rgba(0,0,255,0.5)' -export const DEFAULT_LENS_ZOOM_LEVEL = 3 / 2 -export const DEFAULT_POINTER_POSITION = { x: 0.5, y: 0.5 } -export const PDF_RESOLUTION = 1.5 * 10 ** 6 // target resolution 1.5 Mpx -export const MAX_PDF_SCALE = 5.5 // max DPI = 500 +const STROKE_COLOR = '#FF0000'; +const SELECTION_FILL_COLOR = 'rgba(0,0,255,0.5)'; +export const DEFAULT_LENS_ZOOM_LEVEL = 3 / 2; +export const DEFAULT_POINTER_POSITION = { x: 0.5, y: 0.5 }; +export const PDF_RESOLUTION = 1.5 * 10 ** 6; // target resolution 1.5 Mpx +export const MAX_PDF_SCALE = 5.5; // max DPI = 500 export const DEFAULT_DATA: AnnotationData = { image: null, shapes: [], orientation: 0, -} +}; export const DEFAULT_ANNOTATION_VIEWER_OPTIONS = { enableSelection: false, selectionRectConfig: { fill: SELECTION_FILL_COLOR, }, - onMouseLeave: (polygon: Line) => { - const layer = polygon.getLayer() - polygon.setAttr('fill', 'transparent') - layer?.batchDraw() + onMouseLeave: (polygon: Line): void => { + const layer = polygon.getLayer(); + polygon.setAttr('fill', 'transparent'); + layer?.batchDraw(); }, - onMouseEnter: (polygon: Line) => { - const stroke = polygon.getAttr('stroke') - polygon.setAttr('fill', `${stroke}40`) - polygon.draw() + onMouseEnter: (polygon: Line): void => { + const stroke = polygon.stroke(); + const fill = typeof stroke === 'string' ? `${stroke}40` : stroke; + polygon.setAttr('fill', fill); + polygon.draw(); }, shapeConfig: { stroke: STROKE_COLOR, @@ -51,7 +52,7 @@ export const DEFAULT_ANNOTATION_VIEWER_OPTIONS = { max: 10, defaultZoom: 1, }, -} +}; export const DEFAULT_ANNOTATION_LENS_OPTIONS = { shapeConfig: { @@ -59,4 +60,4 @@ export const DEFAULT_ANNOTATION_LENS_OPTIONS = { strokeWidth: 1, listening: false, }, -} +}; diff --git a/src/common/types.ts b/src/common/types.ts index ce2b5fa..f98081e 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,92 +1,98 @@ -import { CSSProperties } from 'react' -import Konva from 'konva' -import { Line, LineConfig } from 'konva/lib/shapes/Line' -import { RectConfig } from 'konva/lib/shapes/Rect' +import type { CSSProperties } from 'react'; +import type Konva from 'konva'; +import type { Line, LineConfig } from 'konva/lib/shapes/Line'; +import type { RectConfig } from 'konva/lib/shapes/Rect'; +import type { Stage } from 'konva/lib/Stage'; -import { Stage } from '..' +export type AnnotationShape = T & { + id: string; + coordinates: number[][]; + config?: LineConfig; +}; -export type AnnotationShape = T & { - id: string - coordinates: number[][] - config?: LineConfig -} - -export type Orientation = 0 | 90 | 180 | 270 +export type Orientation = 0 | 90 | 180 | 270; export type BaseOptions = { - shapeConfig?: LineConfig -} + shapeConfig?: LineConfig; +}; export type AnnotationLayers = { - shapes: Konva.Layer - image: Konva.Layer -} + shapes: Konva.Layer; + image: Konva.Layer; +}; -export type AnnotationLensOptions = BaseOptions +export type AnnotationLensOptions = BaseOptions; export type AnnotationViewerOptions = BaseOptions & { - selectionRectConfig?: RectConfig - enableSelection?: boolean - onMouseEnter?: (polygon: Line) => void - onMouseLeave?: (polygon: Line) => void - onClick?: (polygon: Line) => void + selectionRectConfig?: RectConfig; + enableSelection?: boolean; + onMouseEnter?: (polygon: Line) => void; + onMouseLeave?: (polygon: Line) => void; + onClick?: (polygon: Line) => void; + /** + * Wheel zoom settings. All factors are relative to the fitted image + * (1 = image fits the container), independently of its pixel size. + */ zoom?: { - modifier: number - max: number - defaultZoom: number - } -} + /** Multiplier applied per wheel step. */ + modifier: number; + /** Highest magnification reachable through the wheel. */ + max: number; + /** @deprecated Unused since 1.3.3; kept so existing option objects still type-check. */ + defaultZoom: number; + }; +}; export type ImageBoundingBox = { - x: number - y: number - width: number - height: number - scale: number -} + x: number; + y: number; + width: number; + height: number; + scale: number; +}; export type PointerPosition = { - x: number - y: number -} + x: number; + y: number; +}; export type AnnotationData = { - image?: string | null - shapes?: AnnotationShape[] - orientation?: Orientation -} + image?: string | null; + shapes?: AnnotationShape[]; + orientation?: Orientation; +}; export type ImageData = { - element: HTMLImageElement - shape: Konva.Image -} + element: HTMLImageElement; + shape: Konva.Image; +}; export type ZoomOptions = { - scale?: number - position?: PointerPosition -} -export interface AnnotationViewerProps { - id?: string - getPointerPosition?: (data: PointerPosition) => void - data?: AnnotationData - getStage?: (stage: Stage) => void - onShapeMultiSelect?: (shapes: AnnotationShape[]) => void - onShapeClick?: (shape: AnnotationShape) => void - onShapeMouseEnter?: (shape: AnnotationShape) => void - onShapeMouseLeave?: (shape: AnnotationShape) => void - options?: AnnotationViewerOptions - style?: CSSProperties - zoomScale?: number - customStagePosition?: PointerPosition - customZoomLevel?: number -} + scale?: number; + position?: PointerPosition; +}; +export type AnnotationViewerProps = { + id?: string; + getPointerPosition?: (data: PointerPosition) => void; + data?: AnnotationData; + getStage?: (stage: Stage) => void; + onShapeMultiSelect?: (shapes: AnnotationShape[]) => void; + onShapeClick?: (shape: AnnotationShape) => void; + onShapeMouseEnter?: (shape: AnnotationShape) => void; + onShapeMouseLeave?: (shape: AnnotationShape) => void; + options?: AnnotationViewerOptions; + style?: CSSProperties; + zoomScale?: number; + customStagePosition?: PointerPosition; + customZoomLevel?: number; +}; -export interface AnnotationLensProps { - id?: string - zoomLevel?: number - data?: AnnotationData - pointerPosition?: PointerPosition - getStage?: (stage: Stage) => void - style?: CSSProperties - options?: AnnotationLensOptions -} +export type AnnotationLensProps = { + id?: string; + zoomLevel?: number; + data?: AnnotationData; + pointerPosition?: PointerPosition; + getStage?: (stage: Stage) => void; + style?: CSSProperties; + options?: AnnotationLensOptions; +}; diff --git a/src/components/AnnotationLens.spec.tsx b/src/components/AnnotationLens.spec.tsx index 2379acc..71862bb 100644 --- a/src/components/AnnotationLens.spec.tsx +++ b/src/components/AnnotationLens.spec.tsx @@ -1,34 +1,36 @@ -import React, { useState } from 'react' -import anotherDummyImage from 'cypress/assets/another-demo.jpg' -import dummyImage from 'cypress/assets/demo.jpg' -import { dummyShapes } from 'cypress/assets/shapes' +import React, { useState } from 'react'; +import anotherDummyImage from 'cypress/assets/another-demo.jpg'; +import dummyImageHEIC from 'cypress/assets/demo.heic'; +import dummyImage from 'cypress/assets/demo.jpg'; +import dummyImageTIFF from 'cypress/assets/demo.tiff'; +import { dummyShapes } from 'cypress/assets/shapes'; -import { AnnotationData } from '@/common/types' +import type { AnnotationData, PointerPosition } from '@/common/types'; -import AnnotationLens from './AnnotationLens' +import AnnotationLens from './AnnotationLens'; -const containerHeight = 600 -const containerWidth = 600 +const CONTAINER_HEIGHT = 600; +const CONTAINER_WIDTH = 600; type TesterProps = { - containerWidth: number - containerHeight: number - id?: string -} + containerWidth: number; + containerHeight: number; + id?: string; +}; export const AnnotationLensPointerPositionTester = ({ id = 'annotationLens', containerHeight, containerWidth, }: TesterProps) => { - const [pointerPosition, setPointerPosition] = useState({ x: 0, y: 0 }) + const [pointerPosition, setPointerPosition] = useState({ x: 0, y: 0 }); const onMove = () => { - setPointerPosition((pointerPosition) => ({ - x: pointerPosition.x + 0.2, - y: pointerPosition.y + 0.2, - })) - } + setPointerPosition((pp: PointerPosition) => ({ + x: pp.x + 0.2, + y: pp.y + 0.2, + })); + }; return (
- ) -} + ); +}; export const AnnotationLensStateTester = ({ containerHeight, @@ -52,20 +54,20 @@ export const AnnotationLensStateTester = ({ const [data, setData] = useState({ image: dummyImage, shapes: dummyShapes, - }) + }); const passSameData = () => { - setData({ image: dummyImage, shapes: dummyShapes }) - } + setData({ image: dummyImage, shapes: dummyShapes }); + }; const passDifferentImage = () => { - setData({ image: anotherDummyImage, shapes: dummyShapes }) - } + setData({ image: anotherDummyImage, shapes: dummyShapes }); + }; const passDifferentShapes = () => { - setData({ image: dummyImage, shapes: dummyShapes.slice(3) }) - } + setData({ image: dummyImage, shapes: dummyShapes.slice(3) }); + }; const passDifferentOrientation = () => { - setData({ image: dummyImage, shapes: dummyShapes, orientation: 90 }) - } + setData({ image: dummyImage, shapes: dummyShapes, orientation: 90 }); + }; return (
- ) -} + ); +}; describe('AnnotationLens', () => { - const componentId = 'annotationLens' + const componentId = 'annotationLens'; it('mount correctly', () => { cy.mount( , ).then(() => { - cy.wait(500) - cy.pause() + cy.wait(500); + cy.pause(); cy.get('canvas') .should('have.length', 2) .each(($canvas) => { - const canvasWidth = $canvas.width() - const canvasHeight = $canvas.height() - cy.wrap(canvasWidth).should('equal', containerWidth) - cy.wrap(canvasHeight).should('equal', containerHeight) - }) - cy.get(`#${componentId}`).matchImageSnapshot(componentId + '.default') + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${componentId}`).matchImageSnapshot(`${componentId}.default`); cy.get('[data-cy="move-pointer"]') .click() .then(() => { cy.get(`#${componentId}`).matchImageSnapshot( - componentId + '.pointer-move.1', - ) - }) + `${componentId}.pointer-move.1`, + ); + }); cy.get('[data-cy="move-pointer"]') .click() .then(() => { cy.get(`#${componentId}`).matchImageSnapshot( - componentId + '.pointer-move.2', - ) - }) + `${componentId}.pointer-move.2`, + ); + }); cy.get('[data-cy="move-pointer"]') .click() .then(() => { cy.get(`#${componentId}`).matchImageSnapshot( - componentId + '.pointer-move.3', - ) - }) - }) - }) + `${componentId}.pointer-move.3`, + ); + }); + }); + }); it('support state changes', () => { cy.mount( , ).then(() => { - cy.get('[data-cy="same-data"]').click() - cy.wait(400) - cy.get(`#${componentId}`).matchImageSnapshot('annotationLens.same-data') - cy.get('[data-cy="different-image"]').click() - cy.wait(400) + cy.get('[data-cy="same-data"]').click(); + cy.wait(400); + cy.get(`#${componentId}`).matchImageSnapshot('annotationLens.same-data'); + cy.get('[data-cy="different-image"]').click(); + cy.wait(400); cy.get(`#${componentId}`).matchImageSnapshot( 'annotationLens.different-image', - ) - cy.get('[data-cy="different-shapes"]').click() - cy.wait(400) + ); + cy.get('[data-cy="different-shapes"]').click(); + cy.wait(400); cy.get(`#${componentId}`).matchImageSnapshot( 'annotationLens.different-shapes', - ) - cy.get('[data-cy="different-orientation"]').click() - cy.wait(400) + ); + cy.get('[data-cy="different-orientation"]').click(); + cy.wait(400); cy.get(`#${componentId}`).matchImageSnapshot( 'annotationLens.different-orientation', - ) - }) - }) -}) + ); + }); + }); + + it('shows a TIFF image in the lens', () => { + cy.mount( + , + ); + cy.get(`#${componentId}`).should('be.visible'); + cy.wait(1000); + cy.get('canvas') + .should('have.length', 2) + .each(($canvas) => { + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${componentId}`).matchImageSnapshot('annotationLens.tiff.file'); + }); + + it('shows a HEIC image in the lens', () => { + cy.mount( + , + ); + cy.get(`#${componentId}`).should('be.visible'); + cy.wait(1000); + cy.get('canvas') + .should('have.length', 2) + .each(($canvas) => { + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${componentId}`).matchImageSnapshot('annotationLens.heic.file'); + }); +}); diff --git a/src/components/AnnotationLens.tsx b/src/components/AnnotationLens.tsx index 03d743c..b83379e 100644 --- a/src/components/AnnotationLens.tsx +++ b/src/components/AnnotationLens.tsx @@ -1,6 +1,11 @@ -import React, { useEffect, useRef } from 'react' -import Konva from 'konva' -import { v4 as uuidv4 } from 'uuid' +import React, { + useEffect, + useId, + useRef, + useState, + type ReactElement, +} from 'react'; +import Konva from 'konva'; import { DEFAULT_ANNOTATION_LENS_OPTIONS, @@ -8,137 +13,154 @@ import { DEFAULT_LENS_ZOOM_LEVEL, DEFAULT_POINTER_POSITION, DEFAULT_STYLE, - KONVA_REFS, -} from '@/common/constants' -import { + KonvaRefs, +} from '@/common/constants'; +import type { + AnnotationLensOptions, AnnotationLensProps, + AnnotationShape, ImageBoundingBox, ImageData, -} from '@/common/types' +} from '@/common/types'; + +import { mapShapesToPolygons } from '@/utils/canvas'; +import { handleResizeImage, prepareImage } from '@/utils/image'; +import { clearLayers } from '@/utils/layer'; +import { rotateImage } from '@/utils/orientation'; +import { handleLensZoom } from '@/utils/zoom'; -import { mapShapesToPolygons } from '@/utils/canvas' -import { handleResizeImage } from '@/utils/image' -import { clearLayers } from '@/utils/layer' -import { rotateImage } from '@/utils/orientation' -import { handleLensZoom } from '@/utils/zoom' +const drawLensShapes = ( + shapesLayer: Konva.Layer, + shapes: AnnotationShape[] | undefined, + imageBoundingBox: ImageBoundingBox | null, + options: AnnotationLensOptions, +): void => { + shapesLayer.destroyChildren(); + if (!shapes) { + return; + } + mapShapesToPolygons(shapesLayer, shapes, false, imageBoundingBox, options); + shapesLayer.batchDraw(); +}; export default function AnnotationLens({ - id: containerId = uuidv4(), + id, zoomLevel = DEFAULT_LENS_ZOOM_LEVEL, pointerPosition = DEFAULT_POINTER_POSITION, getStage, style = {}, options: customOptions = {}, data = DEFAULT_DATA, -}: AnnotationLensProps) { - const options = { +}: AnnotationLensProps): ReactElement { + const generatedId = useId(); + const containerId = id ?? generatedId; + const options: AnnotationLensOptions = { ...DEFAULT_ANNOTATION_LENS_OPTIONS, ...customOptions, - } - const annotationData = useRef(DEFAULT_DATA) + }; + const optionsKey = JSON.stringify(options); + const optionsRef = useRef(options); + useEffect(() => { + optionsRef.current = options; + }); const imageDataObject = useRef({ element: new Image(), shape: new Konva.Image({ image: new Image() }), - }) - const containerRef = useRef(null) - + }); + const containerRef = useRef(null); const layersObject = useRef({ - shapes: new Konva.Layer({ id: KONVA_REFS.shapesLayer, listening: false }), + shapes: new Konva.Layer({ id: KonvaRefs.ShapesLayer, listening: false }), image: new Konva.Layer({ listening: false }), - }) - const stageObject = useRef(null) - const imageBoundingBoxObject = useRef(null) - useEffect(() => { - stageObject.current = new Konva.Stage({ - container: containerId, - listening: false, - }) - getStage?.(stageObject.current) - stageObject.current.add( - layersObject.current.image, - layersObject.current.shapes, - ) - layersObject.current.image.add(imageDataObject.current.shape) - }, []) - - const onZoomChange = () => { - handleLensZoom( - stageObject.current, - imageBoundingBoxObject.current, - pointerPosition, - zoomLevel, - ) - } + }); + const stageObject = useRef(null); + const imageBoundingBoxObject = useRef(null); + const [imageVersion, setImageVersion] = useState(0); + const getStageRef = useRef(getStage); useEffect(() => { - onZoomChange() - }, [pointerPosition, zoomLevel]) + getStageRef.current = getStage; + }); useEffect(() => { - if (!data.image) { - clearLayers(layersObject.current) - imageDataObject.current.element = new Image() - imageDataObject.current.shape.image(imageDataObject.current.element) - annotationData.current = data - return - } - if ( - annotationData.current.image !== data.image || - data.orientation !== annotationData.current.orientation - ) { - annotationData.current = data - loadImage() - } else { - annotationData.current = data - drawShapes() + const container = containerRef.current; + if (!container) { + return; } - }, [data]) + const stage = new Konva.Stage({ container, listening: false }); + stageObject.current = stage; + stage.add(layersObject.current.image, layersObject.current.shapes); + layersObject.current.image.add(imageDataObject.current.shape); + getStageRef.current?.(stage); + return () => { + stage.destroy(); + stageObject.current = null; + }; + }, []); - const loadImage = async () => { - imageDataObject.current.element.onload = () => { - imageDataObject.current.shape.image(imageDataObject.current.element) - resizeImage() + const { image, orientation, shapes } = data; + + useEffect(() => { + if (image === undefined || image === null || image === '') { + clearLayers(layersObject.current); + imageDataObject.current.element = new Image(); + imageDataObject.current.shape.image(imageDataObject.current.element); + imageBoundingBoxObject.current = null; + return; } - if (annotationData.current.orientation) { + const controller = new AbortController(); + const { signal } = controller; + void (async () => { try { - const image = await rotateImage(annotationData.current) - imageDataObject.current.element.src = image + const prepared = await prepareImage(image); + signal.throwIfAborted(); + const src = await rotateImage(prepared, orientation); + if (signal.aborted) { + return; + } + const element = new Image(); + element.onload = () => { + if (signal.aborted) { + return; + } + imageDataObject.current.element = element; + imageDataObject.current.shape.image(element); + imageBoundingBoxObject.current = + handleResizeImage( + stageObject.current, + containerRef.current, + imageDataObject.current, + ) ?? null; + setImageVersion((v) => v + 1); + }; + element.src = src; } catch (error) { - console.error(error) + if (!signal.aborted) { + console.error(error); + } } - } else { - imageDataObject.current.element.src = annotationData.current.image! - } - } + })(); + return () => { + controller.abort(); + }; + }, [image, orientation]); - const drawShapes = () => { - layersObject.current.shapes.destroyChildren() - if (!annotationData.current.shapes) { - return - } - mapShapesToPolygons( - layersObject.current.shapes, - annotationData.current.shapes, - false, + useEffect(() => { + handleLensZoom( + stageObject.current, imageBoundingBoxObject.current, - options, - ) - - layersObject.current.shapes.batchDraw() - } + pointerPosition, + zoomLevel, + ); + }, [pointerPosition, zoomLevel, imageVersion]); - const resizeImage = () => { - const imageBoundingBox = handleResizeImage( - stageObject.current, - containerRef.current, - imageDataObject.current, - ) - if (imageBoundingBox) { - imageBoundingBoxObject.current = imageBoundingBox - onZoomChange() - drawShapes() - } - } + useEffect(() => { + drawLensShapes( + layersObject.current.shapes, + shapes, + imageBoundingBoxObject.current, + optionsRef.current, + ); + }, [shapes, optionsKey, imageVersion]); return (
- ) + ); } diff --git a/src/components/AnnotationViewer.spec.tsx b/src/components/AnnotationViewer.spec.tsx index 851df16..f242095 100644 --- a/src/components/AnnotationViewer.spec.tsx +++ b/src/components/AnnotationViewer.spec.tsx @@ -1,50 +1,52 @@ -import React, { useState } from 'react' -import anotherDummyImage from 'cypress/assets/another-demo.jpg' -import dummyImageHEIC from 'cypress/assets/demo.heic' -import dummyImage from 'cypress/assets/demo.jpg' -import dummyImageTIFF from 'cypress/assets/demo.tiff' +import React, { useState } from 'react'; +import anotherDummyImage from 'cypress/assets/another-demo.jpg'; +import dummyImageHEIC from 'cypress/assets/demo.heic'; +import dummyImage from 'cypress/assets/demo.jpg'; +import dummyImageTIFF from 'cypress/assets/demo.tiff'; +import { dummyShapes } from 'cypress/assets/shapes'; +import Konva from 'konva'; -import { AnnotationData, AnnotationShape } from '@/common/types' +import { KonvaRefs } from '@/common/constants'; +import type { AnnotationData } from '@/common/types'; -import { dummyShapes } from '../../cypress/assets/shapes' -import AnnotationViewer from './AnnotationViewer' +import AnnotationViewer from './AnnotationViewer'; const dummyImageURL = - 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/ReceiptSwiss.jpg/1280px-ReceiptSwiss.jpg' + 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/ReceiptSwiss.jpg/1280px-ReceiptSwiss.jpg'; -const containerHeight = 800 -const containerWidth = 700 -const containerId = 'annotationViewer' +const CONTAINER_HEIGHT = 800; +const CONTAINER_WIDTH = 700; +const containerId = 'annotationViewer'; export const AnnotationViewerStateTester = ({ containerHeight, containerWidth, id = containerId, }: { - containerWidth: number - containerHeight: number - id?: string + containerWidth: number; + containerHeight: number; + id?: string; }) => { const [data, setData] = useState({ image: dummyImage, shapes: dummyShapes, - }) + }); const passSameData = () => { - setData({ image: dummyImage, shapes: dummyShapes }) - } + setData({ image: dummyImage, shapes: dummyShapes }); + }; const passDifferentImage = () => { - setData({ image: anotherDummyImage, shapes: dummyShapes }) - } + setData({ image: anotherDummyImage, shapes: dummyShapes }); + }; const passDifferentShapes = () => { - setData({ image: dummyImage, shapes: dummyShapes.slice(3) }) - } + setData({ image: dummyImage, shapes: dummyShapes.slice(3) }); + }; const passDifferentOrientation = () => { - setData({ image: dummyImage, shapes: dummyShapes, orientation: 90 }) - } + setData({ image: dummyImage, shapes: dummyShapes, orientation: 90 }); + }; const passEmptyImage = () => { - setData({ shapes: dummyShapes }) - } + setData({ shapes: dummyShapes }); + }; return (
- ) -} + ); +}; describe('AnnotationViewer', () => { it('shows an JPG image in the canvas', () => { cy.mount( , - ) - cy.get(`#${containerId}`).should('be.visible') - cy.wait(1000) + ); + cy.get(`#${containerId}`).should('be.visible'); + cy.wait(1000); cy.get('canvas') .should('have.length', 2) .each(($canvas) => { - const canvasWidth = $canvas.width() - const canvasHeight = $canvas.height() - cy.wrap(canvasWidth).should('equal', containerWidth) - cy.wrap(canvasHeight).should('equal', containerHeight) - }) - cy.get(`#${containerId}`).matchImageSnapshot('jpg.file') - }) + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${containerId}`).matchImageSnapshot('jpg.file'); + }); it('shows a TIFF image in the canvas', () => { cy.mount( , - ) - cy.get(`#${containerId}`).should('be.visible') - cy.wait(1000) + ); + cy.get(`#${containerId}`).should('be.visible'); + cy.wait(1000); cy.get('canvas') .should('have.length', 2) .each(($canvas) => { - const canvasWidth = $canvas.width() - const canvasHeight = $canvas.height() - cy.wrap(canvasWidth).should('equal', containerWidth) - cy.wrap(canvasHeight).should('equal', containerHeight) - }) - cy.get(`#${containerId}`).matchImageSnapshot('tiff.file') - }) + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${containerId}`).matchImageSnapshot('tiff.file'); + }); it('shows a HEIC image in the canvas', () => { cy.mount( , - ) - cy.get(`#${containerId}`).should('be.visible') - cy.wait(1000) + ); + cy.get(`#${containerId}`).should('be.visible'); + cy.wait(1000); cy.get('canvas') .should('have.length', 2) .each(($canvas) => { - const canvasWidth = $canvas.width() - const canvasHeight = $canvas.height() - cy.wrap(canvasWidth).should('equal', containerWidth) - cy.wrap(canvasHeight).should('equal', containerHeight) - }) - cy.get(`#${containerId}`).matchImageSnapshot('heic.file') - }) + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${containerId}`).matchImageSnapshot('heic.file'); + }); it('shows a remote image in the canvas', () => { cy.mount( , - ) - cy.get(`#${containerId}`).should('be.visible') - cy.wait(1000) + ); + cy.get(`#${containerId}`).should('be.visible'); + cy.wait(1000); cy.get('canvas') .should('have.length', 2) .each(($canvas) => { - const canvasWidth = $canvas.width() - const canvasHeight = $canvas.height() - cy.wrap(canvasWidth).should('equal', containerWidth) - cy.wrap(canvasHeight).should('equal', containerHeight) - }) - cy.get(`#${containerId}`).matchImageSnapshot('remote.file') - }) + const canvasWidth = $canvas.width(); + const canvasHeight = $canvas.height(); + cy.wrap(canvasWidth).should('equal', CONTAINER_WIDTH); + cy.wrap(canvasHeight).should('equal', CONTAINER_HEIGHT); + }); + cy.get(`#${containerId}`).matchImageSnapshot('remote.file'); + }); it('zoom correctly', () => { cy.mount( , - ) - cy.wait(1000) + ); + cy.wait(1000); cy.get(`#${containerId}`) .trigger('wheel', { deltaY: -60, @@ -187,85 +185,47 @@ describe('AnnotationViewer', () => { }) .trigger('wheel', { deltaY: -60, - }) - cy.wait(200) - cy.get(`#${containerId}`).matchImageSnapshot('zoomed') - }) + }); + cy.wait(200); + cy.get(`#${containerId}`).matchImageSnapshot('zoomed'); + }); it('handle events correctly', () => { - const events = { - onShapeClick: (shape: AnnotationShape) => { - console.log(shape) - }, - onShapeMouseEnter: (shape: AnnotationShape) => { - console.log(shape) - }, - onShapeMouseLeave: (shape: AnnotationShape) => { - console.log(shape) - }, - } - const clickSpy = cy.spy(events, 'onShapeClick').withArgs(dummyShapes[1]) - const mouseEnterSpy = cy - .spy(events, 'onShapeMouseEnter') - .withArgs(dummyShapes[1]) - const mouseLeaveSpy = cy - .spy(events, 'onShapeMouseLeave') - .withArgs(dummyShapes[1]) - + const onShapeClick = cy.spy().as('onShapeClick'); + const onShapeMouseEnter = cy.spy().as('onShapeMouseEnter'); + const onShapeMouseLeave = cy.spy().as('onShapeMouseLeave'); cy.mount( , - { - log: true, - }, - ) - cy.wait(1000) - cy.get(`#${containerId}`) - .click(350, 50) - .then(($container) => { - cy.wait(200) - cy.wrap($container).matchImageSnapshot('shapeClicked') - expect(clickSpy).to.be.calledOnce - }) + ); + cy.wait(1000); - it('should handle click event', () => { - cy.get(`#${containerId}`) - .click(350, 50) - .then(($container) => { - cy.wait(200) - cy.wrap($container).matchImageSnapshot('shapeClicked') - expect(clickSpy).to.be.calledOnce - }) - }) - it('should handle mouse enter event', () => { - cy.get(`#${containerId}`) - .trigger('mouseenter', { - clientX: 350, - clientY: 50, - }) - .then(($container) => { - cy.wait(200) - cy.wrap($container).matchImageSnapshot('shapeMouseEnter') - expect(mouseEnterSpy).to.be.calledOnce - }) - }) - it('should handle mouse leave event', () => { - cy.get(`#${containerId}`) - .trigger('mouseleave', 350, 50) - .then(($container) => { - cy.wait(200) - cy.wrap($container).matchImageSnapshot('shapeMouseLeave') - expect(mouseLeaveSpy).to.be.calledOnce - }) - }) - }) + cy.get(`#${containerId} .konvajs-content`).trigger('mousemove', 350, 50); + cy.get('@onShapeMouseEnter').should( + 'have.been.calledOnceWith', + dummyShapes[1], + ); + + cy.get(`#${containerId}`).click(350, 50); + cy.get('@onShapeClick').should('have.been.calledOnceWith', dummyShapes[1]); + + cy.get(`#${containerId} .konvajs-content`).trigger('mousemove', 10, 10); + cy.get('@onShapeMouseLeave').should( + 'have.been.calledOnceWith', + dummyShapes[1], + ); + cy.get('@onShapeMouseEnter').should('have.been.calledOnce'); + cy.get(`#${containerId}`).matchImageSnapshot('shapeClicked'); + }); it('support multi selection', () => { - const onShapeMultiSelectSpy = cy.spy().as('onShapeMultiSelectSpy') + const onShapeMultiSelectSpy = cy.spy().as('onShapeMultiSelectSpy'); cy.mount( { }} id={containerId} data={{ image: dummyImage, shapes: dummyShapes }} - style={{ height: containerHeight, width: containerWidth }} + style={{ height: CONTAINER_HEIGHT, width: CONTAINER_WIDTH }} onShapeMultiSelect={onShapeMultiSelectSpy} />, ).then(() => { @@ -286,22 +246,63 @@ describe('AnnotationViewer', () => { .trigger('keydown', { altKey: true, ctrlKey: true }) .trigger('mousedown', { which: 1, clientX: 10, clientY: 10 }) .trigger('mousemove', { which: 1, clientX: 600, clientY: 300 }) - .matchImageSnapshot('multi-select') + .matchImageSnapshot('multi-select'); cy.get(`#${containerId}`) .trigger('mouseup') .trigger('keyup', { altKey: true, ctrlKey: true }) .then(() => { - cy.wait(200) + cy.wait(200); cy.get('@onShapeMultiSelectSpy').should( 'have.been.calledOnceWithExactly', dummyShapes.slice(0, 2), - ) - }) - }) - }) + ); + }); + }); + }); + + it('does not select shapes when enableSelection is not set', () => { + const onShapeMultiSelectSpy = cy.spy().as('onShapeMultiSelectSpy'); + let stage: Konva.Stage | null = null; + + cy.mount( + { + stage = s; + }} + />, + ); + cy.wait(1000); + cy.get(`#${containerId}`) + .children() + .trigger('keydown', { altKey: true, ctrlKey: true }) + .trigger('mousedown', { which: 1, clientX: 10, clientY: 10 }) + .trigger('mousemove', { which: 1, clientX: 600, clientY: 300 }); + cy.get(`#${containerId}`) + .trigger('mouseup') + .trigger('keyup', { altKey: true, ctrlKey: true }); + cy.wait(200); + cy.get('@onShapeMultiSelectSpy') + .should('not.have.been.called') + .then(() => { + const shapesLayer = stage?.findOne(`#${KonvaRefs.ShapesLayer}`); + expect(shapesLayer).to.be.instanceOf(Konva.Layer); + expect((shapesLayer as Konva.Layer).find('Rect')).to.have.length(0); + expect((shapesLayer as Konva.Layer).find('Line')).to.have.length( + dummyShapes.length, + ); + }); + }); it('support custom options', () => { - dummyShapes[0].config = { fill: 'green', opacity: 0.2 } + const [firstShape] = dummyShapes; + if (!firstShape) { + throw new Error('fixture has no shapes'); + } + firstShape.config = { fill: 'green', opacity: 0.2 }; cy.mount( { }} id={containerId} data={{ image: dummyImage, shapes: dummyShapes }} - style={{ height: containerHeight, width: containerWidth }} + style={{ height: CONTAINER_HEIGHT, width: CONTAINER_WIDTH }} />, ).then(() => { - cy.wait(200) - cy.get(`#${containerId}`).matchImageSnapshot('custom-options') - }) - }) + cy.wait(200); + cy.get(`#${containerId}`).matchImageSnapshot('custom-options'); + }); + }); // it.only('support custom zoom level', () => { // dummyShapes[0].config = { fill: 'green', opacity: 0.2 } @@ -336,22 +337,22 @@ describe('AnnotationViewer', () => { cy.mount( , ).then(() => { - cy.get('[data-cy="same-data"]').click() - cy.wait(400) - cy.get(`#${containerId}`).matchImageSnapshot('same-data') - cy.get('[data-cy="different-image"]').click() - cy.wait(400) - cy.get(`#${containerId}`).matchImageSnapshot('different-image') - cy.get('[data-cy="different-shapes"]').click() - cy.wait(400) - cy.get(`#${containerId}`).matchImageSnapshot('different-shapes') - cy.get('[data-cy="different-orientation"]').click() - cy.wait(400) - cy.get(`#${containerId}`).matchImageSnapshot('different-orientation') - }) - }) -}) + cy.get('[data-cy="same-data"]').click(); + cy.wait(400); + cy.get(`#${containerId}`).matchImageSnapshot('same-data'); + cy.get('[data-cy="different-image"]').click(); + cy.wait(400); + cy.get(`#${containerId}`).matchImageSnapshot('different-image'); + cy.get('[data-cy="different-shapes"]').click(); + cy.wait(400); + cy.get(`#${containerId}`).matchImageSnapshot('different-shapes'); + cy.get('[data-cy="different-orientation"]').click(); + cy.wait(400); + cy.get(`#${containerId}`).matchImageSnapshot('different-orientation'); + }); + }); +}); diff --git a/src/components/AnnotationViewer.tsx b/src/components/AnnotationViewer.tsx index 1392418..6b7aefa 100644 --- a/src/components/AnnotationViewer.tsx +++ b/src/components/AnnotationViewer.tsx @@ -1,39 +1,84 @@ -import React, { useEffect, useRef } from 'react' -import Konva from 'konva' -import { KonvaEventObject } from 'konva/lib/Node' -import { v4 as uuidv4 } from 'uuid' +import React, { + useEffect, + useId, + useRef, + useState, + type ReactElement, +} from 'react'; +import Konva from 'konva'; import { DEFAULT_ANNOTATION_VIEWER_OPTIONS, DEFAULT_DATA, DEFAULT_STYLE, - KONVA_REFS, -} from '@/common/constants' -import { + KonvaRefs, +} from '@/common/constants'; +import type { + AnnotationShape, + AnnotationViewerOptions, AnnotationViewerProps, ImageBoundingBox, ImageData, -} from '@/common/types' +} from '@/common/types'; -import { getMousePosition, mapShapesToPolygons } from '@/utils/canvas' +import { getMousePosition, mapShapesToPolygons } from '@/utils/canvas'; import { handleResizeImage, prepareImage, setStageBasedImagePosition, -} from '@/utils/image' -import { clearLayers } from '@/utils/layer' -import { rotateImage } from '@/utils/orientation' +} from '@/utils/image'; +import { clearLayers } from '@/utils/layer'; +import { rotateImage } from '@/utils/orientation'; import { createSelectionRect, onSelectionEnd, onSelectionMove, onSelectionStart, -} from '@/utils/selection' -import useMultiSelection from '@/utils/useMultiSelection' -import { handleStageZoom, handleZoomScale } from '@/utils/zoom' +} from '@/utils/selection'; +import useMultiSelection from '@/utils/useMultiSelection'; +import { getZoomScale, handleStageZoom, handleZoomScale } from '@/utils/zoom'; + +type ShapeCallbacks = Pick< + AnnotationViewerProps, + | 'getPointerPosition' + | 'getStage' + | 'onShapeClick' + | 'onShapeMouseEnter' + | 'onShapeMouseLeave' + | 'onShapeMultiSelect' +>; + +const drawViewerShapes = ( + shapesLayer: Konva.Layer, + shapes: AnnotationShape[] | undefined, + imageBoundingBox: ImageBoundingBox | null, + options: AnnotationViewerOptions, + selectionRect: Konva.Rect, + callbacks: ShapeCallbacks, +): void => { + shapesLayer.destroyChildren(); + if (options.enableSelection === true) { + selectionRect.setAttrs({ ...options.selectionRectConfig }); + shapesLayer.add(selectionRect); + } + if (!shapes) { + return; + } + mapShapesToPolygons( + shapesLayer, + shapes, + true, + imageBoundingBox, + options, + callbacks.onShapeClick, + callbacks.onShapeMouseEnter, + callbacks.onShapeMouseLeave, + ); + shapesLayer.batchDraw(); +}; export default function AnnotationViewer({ - id: containerId = uuidv4(), + id, getPointerPosition, onShapeMouseEnter, onShapeMultiSelect, @@ -45,220 +90,248 @@ export default function AnnotationViewer({ onShapeClick, options: customOptions = {}, data = DEFAULT_DATA, -}: AnnotationViewerProps) { - const options = { +}: AnnotationViewerProps): ReactElement { + const generatedId = useId(); + const containerId = id ?? generatedId; + const options: AnnotationViewerOptions = { ...DEFAULT_ANNOTATION_VIEWER_OPTIONS, ...customOptions, - } - const annotationData = useRef(DEFAULT_DATA) - const isSelectionActiveRef = useRef(false) - const containerRef = useRef(null) - const selectionRectObject = useRef(createSelectionRect(options)) + }; + const optionsKey = JSON.stringify(options); + const isSelectionActiveRef = useRef(false); + const containerRef = useRef(null); + const selectionRectObject = useRef(createSelectionRect(options)); const imageDataObject = useRef({ element: new Image(), shape: new Konva.Image({ image: new Image() }), - }) + }); const layersObject = useRef({ - shapes: new Konva.Layer({ id: KONVA_REFS.shapesLayer }), + shapes: new Konva.Layer({ id: KonvaRefs.ShapesLayer }), image: new Konva.Layer({ listening: false }), - }) - const stageObject = useRef(null) - const imageBoundingBoxObject = useRef(null) - - useMultiSelection({ stage: stageObject.current, isSelectionActiveRef }) + }); + const stageObject = useRef(null); + const imageBoundingBoxObject = useRef(null); + const [imageVersion, setImageVersion] = useState(0); + const optionsRef = useRef(options); + const callbacksRef = useRef({}); useEffect(() => { - initCanvas() - }, []) + optionsRef.current = options; + callbacksRef.current = { + getPointerPosition, + getStage, + onShapeClick, + onShapeMouseEnter, + onShapeMouseLeave, + onShapeMultiSelect, + }; + }); - useEffect(() => { - window.addEventListener('resize', resizeImage) - return () => { - window.removeEventListener('resize', resizeImage) + useMultiSelection({ + stageRef: stageObject, + isSelectionActiveRef, + isEnabled: options.enableSelection === true, + }); + + const fitImageToContainer = (): void => { + const imageBoundingBox = handleResizeImage( + stageObject.current, + containerRef.current, + imageDataObject.current, + ); + if (!imageBoundingBox) { + return; } - }, [data]) + imageBoundingBoxObject.current = imageBoundingBox; + layersObject.current.image.batchDraw(); + setImageVersion((v) => v + 1); + }; useEffect(() => { - if ( - customStagePosition && - stageObject.current && - imageBoundingBoxObject.current - ) { - const stageX = stageObject.current.x() - const stageY = stageObject.current.y() - const zoomScale = stageObject.current.getAttr('zoomScale') - const newPosition = { - x: stageX + customStagePosition.x * zoomScale, - y: stageY + customStagePosition.y * zoomScale, - } - setStageBasedImagePosition({ - imageBoundingBox: imageBoundingBoxObject.current, - stage: stageObject.current, - newPosition, - }) + const container = containerRef.current; + if (!container) { + return; } - }, [customStagePosition]) - - const applyCustomZoomLevel = () => { - if (!customZoomLevel) return + const stage = new Konva.Stage({ container }); + const layers = layersObject.current; + stageObject.current = stage; + stage.add(layers.image, layers.shapes); + layers.image.add(imageDataObject.current.shape); + callbacksRef.current.getStage?.(stage); - handleZoomScale( - stageObject.current, - customZoomLevel, - imageBoundingBoxObject.current, - ) - } + stage.on('wheel', (event) => { + handleStageZoom( + stage, + imageBoundingBoxObject.current, + event, + optionsRef.current, + ); + }); + stage.on('mousemove', () => { + const { getPointerPosition: getPointerPosCallback } = + callbacksRef.current; + if (!getPointerPosCallback) { + return; + } + const mousePointTo = getMousePosition( + stage, + imageBoundingBoxObject.current, + ); + if (mousePointTo) { + getPointerPosCallback(mousePointTo); + } + }); + stage.on('mousedown touchstart', (event) => { + if (optionsRef.current.enableSelection !== true) { + return; + } + onSelectionStart( + event, + layers.shapes, + selectionRectObject.current, + isSelectionActiveRef.current, + ); + }); + stage.on('mousemove touchmove', () => { + if (optionsRef.current.enableSelection !== true) { + return; + } + onSelectionMove(layers.shapes, selectionRectObject.current); + }); + stage.on('mouseup touchend', () => { + if (optionsRef.current.enableSelection !== true) { + return; + } + onSelectionEnd( + layers.shapes, + selectionRectObject.current, + callbacksRef.current.onShapeMultiSelect, + ); + }); + stage.on('dragstart', () => { + if (optionsRef.current.enableSelection !== true) { + return; + } + stage.container().style.cursor = 'grabbing'; + }); + stage.on('dragend', () => { + if (optionsRef.current.enableSelection !== true) { + return; + } + stage.container().style.cursor = 'pointer'; + }); - useEffect(() => applyCustomZoomLevel(), [customZoomLevel]) + return () => { + stage.destroy(); + stageObject.current = null; + }; + }, []); useEffect(() => { - // if image is null or undefined, we should clear everything and wait for the next state change - if (!data.image) { - clearLayers(layersObject.current) - imageDataObject.current.element = new Image() - imageDataObject.current.shape.image(imageDataObject.current.element) - annotationData.current = data - return - } - if ( - annotationData.current.image !== data.image || - data.orientation !== annotationData.current.orientation - ) { - annotationData.current = data - loadImage() - } else { - annotationData.current = data - drawShapes() - } - }, [data]) + window.addEventListener('resize', fitImageToContainer); + return () => { + window.removeEventListener('resize', fitImageToContainer); + }; + }, []); - const initCanvas = () => { - stageObject.current = new Konva.Stage({ - container: containerId, - }) - getStage?.(stageObject.current) - stageObject.current.add( - layersObject.current.image, - layersObject.current.shapes, - ) - stageObject.current.on('wheel', onZoom) - layersObject.current.image.add(imageDataObject.current.shape) - getPointerPosition && - stageObject.current.on('mousemove', () => { - const mousePointTo = getMousePosition( - stageObject.current, - imageBoundingBoxObject.current, - ) - mousePointTo && getPointerPosition(mousePointTo) - }) - if (options.enableSelection) { - layersObject.current.shapes.add(selectionRectObject.current) - stageObject.current.on('mousedown touchstart', (event) => - onSelectionStart( - event, - layersObject.current.shapes, - selectionRectObject.current, - isSelectionActiveRef.current, - ), - ) - stageObject.current.on('mousemove touchmove', () => - onSelectionMove( - layersObject.current.shapes, - selectionRectObject.current, - ), - ) - stageObject.current.on('mouseup touchend', () => - onSelectionEnd( - layersObject.current.shapes, - selectionRectObject.current, - onShapeMultiSelect, - ), - ) + const { image, orientation, shapes } = data; - stageObject.current.on('dragstart', () => { - if (stageObject.current?.container()) { - stageObject.current.container().style.cursor = 'grabbing' - } - }) - stageObject.current.on('dragend', () => { - if (stageObject.current?.container()) { - stageObject.current.container().style.cursor = 'pointer' - } - }) - } - } - - const loadImage = async () => { - annotationData.current.image = await prepareImage( - annotationData.current.image!, - ) - imageDataObject.current.element.onload = () => { - imageDataObject.current.shape.image(imageDataObject.current.element) - resizeImage() - applyCustomZoomLevel() + useEffect(() => { + if (image === undefined || image === null || image === '') { + clearLayers(layersObject.current); + imageDataObject.current.element = new Image(); + imageDataObject.current.shape.image(imageDataObject.current.element); + imageBoundingBoxObject.current = null; + return; } - - if (annotationData.current.orientation) { + const controller = new AbortController(); + const { signal } = controller; + void (async () => { try { - const image = await rotateImage(annotationData.current) - - imageDataObject.current.element.src = image + const prepared = await prepareImage(image); + signal.throwIfAborted(); + const src = await rotateImage(prepared, orientation); + signal.throwIfAborted(); + const element = new Image(); + element.onload = () => { + if (signal.aborted) { + return; + } + imageDataObject.current.element = element; + imageDataObject.current.shape.image(element); + fitImageToContainer(); + }; + element.src = src; } catch (error) { - console.error(error) + if (!signal.aborted) { + console.error(error); + } } - } else { - imageDataObject.current.element.src = annotationData.current.image! - } - } + })(); + return (): void => { + controller.abort(); + }; + }, [image, orientation]); - const drawShapes = () => { - layersObject.current.shapes.destroyChildren() - if (options.enableSelection) { - layersObject.current.shapes.add(selectionRectObject.current) - } - if (!annotationData.current.shapes) { - return - } - - mapShapesToPolygons( + useEffect(() => { + drawViewerShapes( layersObject.current.shapes, - annotationData.current.shapes, - true, + shapes, imageBoundingBoxObject.current, - options, - onShapeClick, - onShapeMouseEnter, - onShapeMouseLeave, - ) - layersObject.current.shapes.batchDraw() - } + { + ...optionsRef.current, + onClick: (polygon) => optionsRef.current.onClick?.(polygon), + onMouseEnter: (polygon) => { + optionsRef.current.onMouseEnter?.(polygon); + }, + onMouseLeave: (polygon) => { + optionsRef.current.onMouseLeave?.(polygon); + }, + }, + selectionRectObject.current, + { + onShapeClick: (shape) => callbacksRef.current.onShapeClick?.(shape), + onShapeMouseEnter: (shape) => + callbacksRef.current.onShapeMouseEnter?.(shape), + onShapeMouseLeave: (shape) => + callbacksRef.current.onShapeMouseLeave?.(shape), + }, + ); + }, [shapes, optionsKey, imageVersion]); - const resizeImage = () => { - const imageBoundingBox = handleResizeImage( - stageObject.current, - containerRef.current, - imageDataObject.current, - ) - if (imageBoundingBox) { - imageBoundingBoxObject.current = imageBoundingBox - layersObject.current.image.batchDraw() - drawShapes() + useEffect(() => { + if (customZoomLevel === undefined) { + return; } - } - - const onZoom = (event: KonvaEventObject) => { - handleStageZoom( + handleZoomScale( stageObject.current, + customZoomLevel, imageBoundingBoxObject.current, - event, - options, - ) - } + ); + }, [customZoomLevel, imageVersion]); + + useEffect(() => { + const stage = stageObject.current; + const imageBoundingBox = imageBoundingBoxObject.current; + if (!customStagePosition || !stage || !imageBoundingBox) { + return; + } + const zoomScale = getZoomScale(stage); + setStageBasedImagePosition({ + imageBoundingBox, + stage, + newPosition: { + x: stage.x() + customStagePosition.x * zoomScale, + y: stage.y() + customStagePosition.y * zoomScale, + }, + }); + }, [customStagePosition]); + return (
- ) + ); } diff --git a/src/components/dynamicZoom.spec.tsx b/src/components/dynamicZoom.spec.tsx index c5795d6..dc67b28 100644 --- a/src/components/dynamicZoom.spec.tsx +++ b/src/components/dynamicZoom.spec.tsx @@ -1,72 +1,95 @@ -import React, { useRef, useState } from 'react' -import dummyImage from 'cypress/assets/demo.jpg' -import { dummyShapes } from 'cypress/assets/shapes' -import { Stage } from 'konva/lib/Stage' +import React, { useRef, useState } from 'react'; +import dummyImage from 'cypress/assets/demo.jpg'; +import { dummyShapes } from 'cypress/assets/shapes'; +import type { Stage } from 'konva/lib/Stage'; -import { AnnotationViewerProps, PointerPosition } from '@/common/types' +import type { AnnotationViewerProps, PointerPosition } from '@/common/types'; -import AnnotationViewer from './AnnotationViewer' +import { getZoomScale } from '@/utils/zoom'; -const containerHeight = 700 -const containerWidth = 700 +import AnnotationViewer from './AnnotationViewer'; -const getStageZoomScale = (stage: Stage) => stage.getAttr('zoomScale') +const containerHeight = 700; +const containerWidth = 700; type AnnotationViewerWithDynamicZoomProps = { - id: AnnotationViewerProps['id'] - data: AnnotationViewerProps['data'] -} + id: AnnotationViewerProps['id']; + data: AnnotationViewerProps['data']; +}; const AnnotationViewerWithDynamicZoom = ({ id, data, }: AnnotationViewerWithDynamicZoomProps) => { - const [customStagePosition, setCustomStagePosition] = useState({ x: 0, y: 0 }) - const stageObject = useRef(null) - const [customZoomLevel, setCustomZoomLevel] = useState(1) + const [customStagePosition, setCustomStagePosition] = useState({ + x: 0, + y: 0, + }); + const stageObject = useRef(null); + const [customZoomLevel, setCustomZoomLevel] = useState(1); const changeScale = (modifier: number) => { - const oldZoomScale = getStageZoomScale(stageObject.current!) - const newScale = modifier * oldZoomScale - setCustomZoomLevel(newScale) - } + const stage = stageObject.current; + if (!stage) { + return; + } + setCustomZoomLevel(modifier * getZoomScale(stage)); + }; const changePosition = (newPosition: PointerPosition) => { - setCustomStagePosition(newPosition) - } + setCustomStagePosition(newPosition); + }; return (
- - @@ -83,8 +106,8 @@ const AnnotationViewerWithDynamicZoom = ({ }} />
- ) -} + ); +}; describe('AnnotationViewer', () => { it('mount correctly', () => { @@ -93,6 +116,6 @@ describe('AnnotationViewer', () => { id="annotationViewer" data={{ image: dummyImage, shapes: dummyShapes }} />, - ) - }) -}) + ); + }); +}); diff --git a/src/declarations.d.ts b/src/declarations.d.ts index d973cc3..7c3df58 100644 --- a/src/declarations.d.ts +++ b/src/declarations.d.ts @@ -1,8 +1,8 @@ -declare module '*.png' -declare module '*.jpeg' -declare module '*.pdf' -declare module '*.jpg' -declare module '*.jpeg' -declare module '*.svg' -declare module '*.tiff' -declare module '*.heic' +declare module '*.png'; +declare module '*.jpeg'; +declare module '*.pdf'; +declare module '*.jpg'; +declare module '*.jpeg'; +declare module '*.svg'; +declare module '*.tiff'; +declare module '*.heic'; diff --git a/src/hooks/usePDFDocument.spec.tsx b/src/hooks/usePDFDocument.spec.tsx new file mode 100644 index 0000000..27d9d7f --- /dev/null +++ b/src/hooks/usePDFDocument.spec.tsx @@ -0,0 +1,493 @@ +import { + Component, + useEffect, + useState, + type JSX, + type ReactNode, +} from 'react'; +import multiPage from 'cypress/assets/multi-page.pdf'; + +import { + usePDFDocument, + type UsePDFDocumentOptions, + type UsePDFDocumentResult, +} from './usePDFDocument'; + +const PAGE_COUNT = 5; + +type HarnessProps = { + source: string | null; + options?: UsePDFDocumentOptions; + onChange: (result: UsePDFDocumentResult) => void; +}; + +/** + * Renders the hook and reports every result to the test, which drives + * `loadPage` through the latest reported value. + */ +const Harness = ({ source, options, onChange }: HarnessProps): JSX.Element => { + const result = usePDFDocument(source, options); + useEffect(() => { + onChange(result); + }, [result, onChange]); + return ( +
    + {result.pages.map((page, index) => ( +
  • + {page === undefined ? 'pending' : 'ready'} +
  • + ))} +
+ ); +}; + +const mountHarness = ( + source: string | null, + options?: UsePDFDocumentOptions, +): { latest: () => UsePDFDocumentResult } => { + let latest: UsePDFDocumentResult | undefined; + cy.mount( + { + latest = result; + }} + />, + ); + return { + latest: () => { + if (latest === undefined) { + throw new Error('hook has not reported yet'); + } + return latest; + }, + }; +}; + +const imageSize = async (url: string): Promise => + await new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => { + resolve(image.naturalWidth * image.naturalHeight); + }; + image.onerror = () => { + reject(new Error(`Could not decode ${url}`)); + }; + image.src = url; + }); + +const readyCount = (result: UsePDFDocumentResult): number => + result.pages.filter((page) => page !== undefined).length; + +class RenderErrorBoundary extends Component< + { children: ReactNode }, + { error: Error | null } +> { + override state: { error: Error | null } = { error: null }; + + static getDerivedStateFromError(error: Error): { error: Error } { + return { error }; + } + + override render(): ReactNode { + if (this.state.error !== null) { + return {this.state.error.name}; + } + return this.props.children; + } +} + +describe('usePDFDocument', () => { + it('stays idle without a source', () => { + const harness = mountHarness(null); + cy.wrap(null).should(() => { + expect(harness.latest().status).to.equal('idle'); + expect(harness.latest().numPages).to.equal(0); + }); + }); + + it('opens the document and prefetches the first pages only', () => { + const harness = mountHarness(multiPage, { prefetch: 2, batch: 2 }); + cy.wrap(null).should(() => { + const result = harness.latest(); + expect(result.status).to.equal('ready'); + expect(result.numPages).to.equal(PAGE_COUNT); + expect(readyCount(result)).to.equal(2); + expect(result.loading.size).to.equal(0); + }); + cy.get('[data-cy=page-0]').should('have.text', 'ready'); + cy.get('[data-cy=page-2]').should('have.text', 'pending'); + }); + + it('loads the whole batch containing a requested page', () => { + const harness = mountHarness(multiPage, { prefetch: 1, batch: 2 }); + cy.wrap(null).should(() => { + expect(readyCount(harness.latest())).to.equal(1); + }); + cy.then(() => { + harness.latest().loadPage(4); + }); + cy.wrap(null).should(() => { + const result = harness.latest(); + expect(result.pages[3]).to.be.a('string'); + expect(result.pages[4]).to.be.a('string'); + expect(result.pages[1]).to.equal(undefined); + expect(result.pages[2]).to.equal(undefined); + }); + }); + + it('marks pages as loading while they render', () => { + const harness = mountHarness(multiPage, { prefetch: 1, batch: 1 }); + cy.wrap(null).should(() => { + expect(harness.latest().status).to.equal('ready'); + }); + cy.then(() => { + harness.latest().loadPage(3); + expect(harness.latest().loadPage).to.be.a('function'); + }); + cy.wrap(null).should(() => { + expect(harness.latest().loading.has(3)).to.equal(true); + }); + cy.wrap(null).should(() => { + expect(harness.latest().loading.has(3)).to.equal(false); + expect(harness.latest().pages[3]).to.be.a('string'); + }); + }); + + it('ignores page indexes outside the document', () => { + const harness = mountHarness(multiPage, { prefetch: 1, batch: 1 }); + cy.wrap(null).should(() => { + expect(harness.latest().status).to.equal('ready'); + }); + cy.then(() => { + harness.latest().loadPage(-1); + harness.latest().loadPage(PAGE_COUNT); + }); + cy.wrap(null).should(() => { + expect(readyCount(harness.latest())).to.equal(1); + expect(harness.latest().error).to.equal(null); + }); + }); + + it('promotes a page still queued behind the prefetch when loadPage asks for it', () => { + const harness = mountHarness(multiPage, { prefetch: 5, batch: 1 }); + cy.wrap(null).should(() => { + expect(harness.latest().status).to.equal('ready'); + expect(harness.latest().loading.size).to.be.greaterThan(0); + }); + cy.then(() => { + harness.latest().loadPage(4); + }); + cy.wrap(null).should(() => { + const result = harness.latest(); + expect(result.pages[4]).to.be.a('string'); + expect(result.pages[2]).to.equal(undefined); + }); + }); + + it('resets to an error without exposing the previous document when maxPages tightens', () => { + const Limiter = (): JSX.Element => { + const [maxPages, setMaxPages] = useState(undefined); + const result = usePDFDocument(multiPage, { prefetch: 1, maxPages }); + return ( +
+ {result.status} + {result.document === null ? 'null' : 'set'} + {String(result.numPages)} + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=limit]').click(); + cy.get('[data-cy=status]').should('have.text', 'error'); + cy.get('[data-cy=doc]').should('have.text', 'null'); + cy.get('[data-cy=count]').should('have.text', '0'); + }); + + it('re-renders every page at the new resolution when the option changes', () => { + const Resizer = (): JSX.Element => { + const [resolution, setResolution] = useState(100_000); + const result = usePDFDocument(multiPage, { prefetch: 2, resolution }); + return ( +
+ {result.status} + + {String(result.pages.filter((page) => page !== undefined).length)} + + {result.pages[0] ?? ''} + {result.pages[PAGE_COUNT - 1] ?? ''} + + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=ready]').should('have.text', '2'); + cy.get('[data-cy=load-last]').click(); + cy.get('[data-cy=ready]').should('have.text', '3'); + cy.get('[data-cy=first]') + .invoke('text') + .then((before) => { + cy.get('[data-cy=grow]').click(); + cy.get('[data-cy=ready]').should('have.text', '3'); + cy.get('[data-cy=last]') + .invoke('text') + .should('match', /^blob:/); + cy.get('[data-cy=first]') + .invoke('text') + .should('not.equal', before) + .then(async (after) => { + const [small, large] = await Promise.all([ + imageSize(before), + imageSize(after), + ]); + expect(large).to.be.greaterThan(small); + }); + }); + }); + + it('switches directly from one source to another without erroring', () => { + const Switcher = (): JSX.Element => { + const [source, setSource] = useState(multiPage); + const result = usePDFDocument(source, { prefetch: 1 }); + return ( +
+ {result.status} + {result.pages[0] ?? ''} + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=first]') + .should('not.have.text', '') + .invoke('text') + .then((before) => { + cy.get('[data-cy=swap]').click(); + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=first]') + .should('not.have.text', '') + .should('not.have.text', before); + cy.get('[data-cy=status]').should('have.text', 'ready'); + }); + }); + + it('aborts pending renders of the previous resolution', () => { + const Resizer = (): JSX.Element => { + const [resolution, setResolution] = useState(100_000); + const result = usePDFDocument(multiPage, { prefetch: 5, resolution }); + return ( +
+ {result.status} + {String(result.loading.size)} + {String(readyCount(result))} + {result.error?.message ?? 'none'} + +
+ ); + }; + cy.mount(); + // Switch while the 5-page prefetch is still rendering. + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=grow]').click(); + cy.get('[data-cy=ready]').should('have.text', String(PAGE_COUNT)); + cy.get('[data-cy=loading]').should('have.text', '0'); + cy.get('[data-cy=error]').should('have.text', 'none'); + }); + + it('ignores renders of the previous document after a source swap mid-prefetch', () => { + const Swapper = (): JSX.Element => { + const [source, setSource] = useState(multiPage); + const result = usePDFDocument(source, { prefetch: 5 }); + return ( +
+ {result.status} + {result.error?.message ?? 'none'} + {String(result.loading.size)} + {String(readyCount(result))} + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=swap]').click(); + cy.get('[data-cy=ready]').should('have.text', String(PAGE_COUNT)); + cy.get('[data-cy=loading]').should('have.text', '0'); + cy.get('[data-cy=error]').should('have.text', 'none'); + cy.get('[data-cy=status]').should('have.text', 'ready'); + }); + + it('returns to idle without error when the source is cleared mid-prefetch', () => { + const Clearer = (): JSX.Element => { + const [source, setSource] = useState(multiPage); + const result = usePDFDocument(source, { prefetch: 5 }); + return ( +
+ {result.status} + {result.error?.message ?? 'none'} + {String(result.loading.size)} + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=clear]').click(); + cy.get('[data-cy=status]').should('have.text', 'idle'); + cy.wait(300); + cy.get('[data-cy=error]').should('have.text', 'none'); + cy.get('[data-cy=loading]').should('have.text', '0'); + }); + + it('ignores non-integer indexes in loadPage and loadRange', () => { + const Probe = (): JSX.Element => { + const result = usePDFDocument(multiPage, { prefetch: 0 }); + return ( +
+ {result.status} + {String(result.loading.size)} + {result.error?.message ?? 'none'} + +
+ ); + }; + cy.mount(); + cy.get('[data-cy=status]').should('have.text', 'ready'); + cy.get('[data-cy=fractional]').click(); + cy.get('[data-cy=loading]').should('have.text', '0'); + cy.get('[data-cy=error]').should('have.text', 'none'); + }); + + it('throws a RangeError for invalid prefetch or batch options', () => { + const Probe = ({ + options, + }: { + options: { prefetch?: number; batch?: number }; + }): JSX.Element => { + usePDFDocument(null, options); + return ok; + }; + const mountProbe = (options: { prefetch?: number; batch?: number }) => { + cy.mount( + + + , + ); + }; + cy.on('uncaught:exception', () => false); + mountProbe({ batch: 0 }); + cy.get('[data-cy=probe]').should('have.text', 'RangeError'); + mountProbe({ prefetch: 1.5 }); + cy.get('[data-cy=probe]').should('have.text', 'RangeError'); + mountProbe({ prefetch: -1 }); + cy.get('[data-cy=probe]').should('have.text', 'RangeError'); + mountProbe({ prefetch: 0, batch: 1 }); + cy.get('[data-cy=probe]').should('have.text', 'ok'); + }); + + it('reports an error status for an unreadable source', () => { + const harness = mountHarness('data:application/pdf;base64,AAAA'); + cy.wrap(null).should(() => { + expect(harness.latest().status).to.equal('error'); + expect(harness.latest().error).to.be.instanceOf(Error); + }); + }); + + it('destroys the document and revokes its pages when the source changes', () => { + const Switcher = (): JSX.Element => { + const [source, setSource] = useState(multiPage); + const result = usePDFDocument(source, { prefetch: 1 }); + return ( +
+ {result.status} + {result.pages[0] ?? ''} + +
+ ); + }; + cy.window().then((win) => { + cy.spy(win.URL, 'revokeObjectURL').as('revoke'); + }); + cy.mount(); + cy.get('[data-cy=first]') + .should('not.have.text', '') + .invoke('text') + .then((url) => { + cy.get('[data-cy=clear]').click(); + cy.get('[data-cy=status]').should('have.text', 'idle'); + cy.get('@revoke').should('have.been.calledWith', url); + }); + }); +}); diff --git a/src/hooks/usePDFDocument.ts b/src/hooks/usePDFDocument.ts new file mode 100644 index 0000000..a33cbb3 --- /dev/null +++ b/src/hooks/usePDFDocument.ts @@ -0,0 +1,377 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { + openPDF, + type PDFDocumentHandle, + type PDFPageOptions, + type PDFSource, +} from '@/utils/pdf'; + +export type UsePDFDocumentOptions = { + /** + * Pages rendered as soon as the document opens. Defaults to 5. Must be a + * non-negative integer. + */ + prefetch?: number; + /** + * Pages rendered together when one of them is requested. Defaults to 3. + * Must be a positive integer. + */ + batch?: number; + /** + * Rendering resolution in pixels, see `PDFPageOptions.resolution`. + * Re-renders on change. + */ + resolution?: number; + /** Rejects documents with more pages, see `OpenPDFOptions.maxPages`. */ + maxPages?: number; +}; + +export type UsePDFDocumentStatus = 'idle' | 'opening' | 'ready' | 'error'; + +export type UsePDFDocumentResult = { + status: UsePDFDocumentStatus; + error: Error | null; + numPages: number; + /** + * One slot per page, indexed from 0. + */ + pages: (string | undefined)[]; + /** Pages (0-based indexes) whose render is queued or in progress. */ + loading: ReadonlySet; + /** + * Renders a batch starting at `index` (0-based). + */ + loadPage: (index: number) => void; + /** + * Renders every page in `[from, to)` (0-based integers, `to` exclusive). + */ + loadRange: (from: number, to: number) => void; + /** Underlying handle for advanced use, `null` until `status` is `ready`. */ + document: PDFDocumentHandle | null; +}; + +const DEFAULT_PREFETCH = 5; +const DEFAULT_BATCH = 3; + +const EMPTY_SET: ReadonlySet = new Set(); + +type State = { + source: PDFSource | null | undefined; + maxPages: number | undefined; + resolution: number | undefined; + status: UsePDFDocumentStatus; + error: Error | null; + document: PDFDocumentHandle | null; + pages: (string | undefined)[]; + loading: ReadonlySet; + /** Indexes to render again after a resolution change. */ + reload: ReadonlySet; +}; + +/** + * Pages requested so far, tagged with the document and resolution they were + * requested for. + */ +type Requested = { + document: PDFDocumentHandle | null; + resolution: number | undefined; + indexes: Set; + controller: AbortController; +}; + +const nextGeneration = ( + previous: Requested, + document: PDFDocumentHandle | null, + resolution: number | undefined, +): Requested => { + previous.controller.abort(); + return { + document, + resolution, + indexes: new Set(), + controller: new AbortController(), + }; +}; + +const initialState = ( + source: PDFSource | null | undefined, + maxPages: number | undefined, + resolution: number | undefined, +): State => ({ + source, + maxPages, + resolution, + status: source === null || source === undefined ? 'idle' : 'opening', + error: null, + document: null, + pages: [], + loading: EMPTY_SET, + reload: EMPTY_SET, +}); + +const toError = (error: unknown): Error => + error instanceof Error ? error : new Error(String(error)); + +/** + * Boundaries of the batch containing `index`: the first `prefetch` pages form + * one block, the remaining pages are grouped `batch` at a time after it. + */ +const batchBounds = ( + index: number, + numPages: number, + prefetch: number, + batch: number, +): [number, number] => { + if (index < prefetch) { + return [0, Math.min(prefetch, numPages)]; + } + const from = prefetch + Math.floor((index - prefetch) / batch) * batch; + return [from, Math.min(from + batch, numPages)]; +}; + +const assertInteger = (name: string, value: number, min: number): void => { + if (!Number.isInteger(value) || value < min) { + throw new RangeError( + `${name} must be an integer >= ${String(min)}, got ${String(value)}`, + ); + } +}; + +/** + * Opens a PDF and renders its pages progressively. + */ +export const usePDFDocument = ( + source: PDFSource | null | undefined, + options: UsePDFDocumentOptions = {}, +): UsePDFDocumentResult => { + const { + prefetch = DEFAULT_PREFETCH, + batch = DEFAULT_BATCH, + resolution, + maxPages, + } = options; + assertInteger('prefetch', prefetch, 0); + assertInteger('batch', batch, 1); + + const [state, setState] = useState(() => + initialState(source, maxPages, resolution), + ); + const requested = useRef({ + document: null, + resolution, + indexes: new Set(), + controller: new AbortController(), + }); + + if (state.source !== source || state.maxPages !== maxPages) { + setState(initialState(source, maxPages, resolution)); + } else if (state.resolution !== resolution) { + setState((previous) => { + const reload = new Set(previous.loading); + previous.pages.forEach((page, index) => { + if (page !== undefined) { + reload.add(index); + } + }); + return { + ...previous, + resolution, + pages: new Array(previous.pages.length).fill( + undefined, + ), + loading: EMPTY_SET, + reload, + }; + }); + } + + useEffect(() => { + if (source === null || source === undefined) { + return undefined; + } + + let handle: PDFDocumentHandle | undefined; + const opening = new AbortController(); + openPDF(source, { maxPages, signal: opening.signal }) + .then(async (opened) => { + if (opening.signal.aborted) { + await opened.destroy(); + return; + } + handle = opened; + setState((previous) => ({ + ...previous, + status: 'ready', + document: opened, + pages: new Array(opened.numPages).fill(undefined), + })); + }) + .catch((reason: unknown) => { + if (!opening.signal.aborted) { + setState((previous) => ({ + ...previous, + status: 'error', + error: toError(reason), + })); + } + }); + + return () => { + opening.abort(); + requested.current = nextGeneration( + requested.current, + null, + requested.current.resolution, + ); + void handle?.destroy(); + }; + }, [source, maxPages]); + + const document = state.document; + + /** Current generation for `document` at `resolution`, started if needed. */ + const generation = useCallback( + (handle: PDFDocumentHandle): Requested => { + if ( + requested.current.document !== handle || + requested.current.resolution !== resolution + ) { + requested.current = nextGeneration( + requested.current, + handle, + resolution, + ); + } + return requested.current; + }, + [resolution], + ); + + const render = useCallback( + (index: number, pageOptions: PDFPageOptions) => { + if (document === null) { + return; + } + const tracker = generation(document); + if (tracker.indexes.has(index)) { + return; + } + tracker.indexes.add(index); + const stale = (): boolean => requested.current !== tracker; + setState((previous) => ({ + ...previous, + loading: new Set(previous.loading).add(index), + })); + document + .getPage(index + 1, { + ...pageOptions, + resolution, + signal: tracker.controller.signal, + }) + .then((image) => { + if (stale()) { + return; + } + setState((previous) => { + const pages = [...previous.pages]; + pages[index] = image; + return { ...previous, pages }; + }); + }) + .catch((reason: unknown) => { + if (stale()) { + return; + } + tracker.indexes.delete(index); + if (toError(reason).name !== 'AbortError') { + setState((previous) => ({ ...previous, error: toError(reason) })); + } + }) + .finally(() => { + if (stale()) { + return; + } + setState((previous) => { + const loading = new Set(previous.loading); + loading.delete(index); + return { ...previous, loading }; + }); + }); + }, + [document, generation, resolution], + ); + + const loadRange = useCallback( + (from: number, to: number) => { + if ( + document === null || + !Number.isInteger(from) || + !Number.isInteger(to) + ) { + return; + } + const start = Math.max(0, from); + const end = Math.min(document.numPages, to); + for (let index = start; index < end; index += 1) { + render(index, { priority: 'low' }); + } + }, + [document, render], + ); + + const loadPage = useCallback( + (index: number) => { + if ( + document === null || + !Number.isInteger(index) || + index < 0 || + index >= document.numPages + ) { + return; + } + const tracker = generation(document); + if (tracker.indexes.has(index)) { + // Already queued: a second high-priority caller promotes the render. + void document + .getPage(index + 1, { + priority: 'high', + resolution, + signal: tracker.controller.signal, + }) + .catch(() => undefined); + } else { + render(index, { priority: 'high' }); + } + loadRange(...batchBounds(index, document.numPages, prefetch, batch)); + }, + [document, generation, render, loadRange, prefetch, batch, resolution], + ); + + useEffect(() => { + if (document !== null) { + loadRange(0, prefetch); + } + }, [document, loadRange, prefetch]); + + const reload = state.reload; + useEffect(() => { + reload.forEach((index) => { + render(index, { priority: 'low' }); + }); + }, [reload, render]); + + return useMemo( + () => ({ + status: state.status, + error: state.error, + numPages: document?.numPages ?? 0, + pages: state.pages, + loading: state.loading, + loadPage, + loadRange, + document, + }), + [state, document, loadPage, loadRange], + ); +}; diff --git a/src/index.ts b/src/index.ts index a072489..74cbfa2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,25 +1,42 @@ -import { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; -import { +import type { AnnotationData, AnnotationLensOptions, AnnotationShape, AnnotationViewerOptions, Orientation, PointerPosition, -} from './common/types' -import AnnotationLens from './components/AnnotationLens' -import AnnotationViewer from './components/AnnotationViewer' +} from './common/types'; +import AnnotationLens from './components/AnnotationLens'; +import AnnotationViewer from './components/AnnotationViewer'; +import { + usePDFDocument, + type UsePDFDocumentOptions, + type UsePDFDocumentResult, + type UsePDFDocumentStatus, +} from './hooks/usePDFDocument'; import { drawLayer, drawShape, drawShapes, setShapeConfig, toBase64, -} from './utils/functions' -import getImagesFromPDF from './utils/getImagesFromPDF' -import getPDFPageCount from './utils/getPDFPageCount' -import { dataURItoBlob } from './utils/image' +} from './utils/functions'; +import getImagesFromPDF from './utils/getImagesFromPDF'; +import getPDFPageCount from './utils/getPDFPageCount'; +import { dataURItoBlob } from './utils/image'; +import { + openPDF, + type OpenPDFOptions, + type PDFDocumentHandle, + type PDFPageOptions, + type PDFPageOutput, + type PDFPagePriority, + type PDFRenderedPage, + type PDFSource, +} from './utils/pdf'; +import { getZoomScale } from './utils/zoom'; export type { Stage, @@ -29,16 +46,30 @@ export type { PointerPosition, Orientation, AnnotationData, -} + OpenPDFOptions, + PDFDocumentHandle, + PDFPageOptions, + PDFPageOutput, + PDFPagePriority, + PDFRenderedPage, + PDFSource, + UsePDFDocumentOptions, + UsePDFDocumentResult, + UsePDFDocumentStatus, +}; export { toBase64, drawShapes, dataURItoBlob, AnnotationLens, AnnotationViewer, + // eslint-disable-next-line @typescript-eslint/no-deprecated -- still part of the public API getImagesFromPDF, getPDFPageCount, + getZoomScale, + openPDF, + usePDFDocument, drawShape, drawLayer, setShapeConfig, -} +}; diff --git a/src/utils/canvas.spec.ts b/src/utils/canvas.spec.ts index 98b3ba7..ee1231d 100644 --- a/src/utils/canvas.spec.ts +++ b/src/utils/canvas.spec.ts @@ -1,28 +1,28 @@ -import { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; -import { ImageBoundingBox } from '@/common/types' +import type { ImageBoundingBox } from '@/common/types'; -import { getMousePosition, scalePointToImage } from '@/utils/canvas' +import { getMousePosition, scalePointToImage } from '@/utils/canvas'; describe('canvas', () => { describe('scalePointToImage', () => { it('should scale the point to the image', () => { - const point = { x: 0.5, y: 0.5 } + const point = { x: 0.5, y: 0.5 }; const imageBoundingBox = { x: 50, y: 0, width: 600, height: 800, scale: 0.781, - } + }; - const expected = { x: 384.12, y: 512.16 } + const expected = { x: 384.12, y: 512.16 }; - const actual = scalePointToImage(point, imageBoundingBox) + const actual = scalePointToImage(point, imageBoundingBox); - expect(actual).to.deep.equal(expected) - }) - }) + expect(actual).to.deep.equal(expected); + }); + }); describe('getMousePosition', () => { it('should return the correct mouse position', () => { @@ -31,7 +31,7 @@ describe('canvas', () => { scaleX: () => 1, x: () => 0, y: () => 0, - } + }; const imageBoundingBox = { x: 50, @@ -39,30 +39,30 @@ describe('canvas', () => { width: 600, height: 800, scale: 1.5, - } + }; - const expected = { x: 0.25, y: 0.38 } + const expected = { x: 0.25, y: 0.38 }; const actual = getMousePosition( stage as unknown as Stage, - imageBoundingBox as ImageBoundingBox, - ) + imageBoundingBox, + ); - expect(actual).to.deep.equal(expected) - }) + expect(actual).to.deep.equal(expected); + }); it('should do nothing if stage or imageBoundingBox are null', () => { - const expected = undefined + const expected = undefined; { - const actual = getMousePosition({} as Stage, null) - expect(actual).to.equal(expected) + const actual = getMousePosition({} as Stage, null); + expect(actual).to.equal(expected); } { - const actual = getMousePosition(null, {} as ImageBoundingBox) - expect(actual).to.equal(expected) + const actual = getMousePosition(null, {} as ImageBoundingBox); + expect(actual).to.equal(expected); } - }) - }) -}) + }); + }); +}); diff --git a/src/utils/canvas.ts b/src/utils/canvas.ts index 6622f8a..a1922e7 100644 --- a/src/utils/canvas.ts +++ b/src/utils/canvas.ts @@ -1,17 +1,22 @@ -import Konva from 'konva' -import { Layer } from 'konva/lib/Layer' -import { Line } from 'konva/lib/shapes/Line' +import Konva from 'konva'; +import type { Layer } from 'konva/lib/Layer'; +import type { Line } from 'konva/lib/shapes/Line'; -import { KONVA_REFS } from '@/common/constants' -import { +import { KonvaRefs } from '@/common/constants'; +import type { AnnotationLensOptions, AnnotationShape, AnnotationViewerOptions, ImageBoundingBox, PointerPosition, -} from '@/common/types' +} from '@/common/types'; -import { roundTo } from './roundTo' +import { roundTo } from './roundTo'; + +const shapeByNode = new WeakMap(); +export const getShapeFromNode = ( + node: Konva.Node, +): AnnotationShape | undefined => shapeByNode.get(node); export const mapShapesToPolygons = ( shapesLayer: Layer, @@ -22,33 +27,33 @@ export const mapShapesToPolygons = ( onClick?: (shape: AnnotationShape) => void, onShapeMouseEnter?: (shape: AnnotationShape) => void, onShapeMouseLeave?: (shape: AnnotationShape) => void, -) => { +): void => { if (!imageBoundingBox) { - return + return; } shapes.forEach((shape: AnnotationShape) => { const polygon = new Konva.Line({ id: shape.id, - name: KONVA_REFS.shape, - points: mapCoordinatesToPoints(shape.coordinates, imageBoundingBox), + name: KonvaRefs.Shape, + points: mapCoordinatesToPoints(shape, imageBoundingBox), closed: true, - ...(options?.shapeConfig || {}), + ...(options.shapeConfig ?? {}), ...shape.config, - shape, - }) - shapesLayer.add(polygon) + }); + shapeByNode.set(polygon, shape); + shapesLayer.add(polygon); if (useEvents) { bindEventToPolygon( polygon, - options as AnnotationViewerOptions, + options, onClick, onShapeMouseEnter, onShapeMouseLeave, - ) + ); } - }) -} + }); +}; const bindEventToPolygon = ( polygon: Line, @@ -56,66 +61,75 @@ const bindEventToPolygon = ( onClick?: (shape: AnnotationShape) => void, onShapeMouseEnter?: (shape: AnnotationShape) => void, onShapeMouseLeave?: (shape: AnnotationShape) => void, -) => { - const stage = polygon.getStage() - const shape = polygon.getAttr('shape') +): void => { + const stage = polygon.getStage(); + const shape = shapeByNode.get(polygon); + if (!shape || !stage) { + return; + } polygon.on('mouseup', (event) => { - event.cancelBubble = true - onClick?.(shape) - options?.onClick?.(polygon) - }) + event.cancelBubble = true; + onClick?.(shape); + options.onClick?.(polygon); + }); polygon.on('mouseleave', function (event) { - event.cancelBubble = true - stage!.container().style.cursor = 'inherit' - options?.onMouseLeave?.(polygon) - onShapeMouseLeave?.(shape) - }) + event.cancelBubble = true; + stage.container().style.cursor = 'inherit'; + options.onMouseLeave?.(polygon); + onShapeMouseLeave?.(shape); + }); polygon.on('mouseenter', function (event) { - event.cancelBubble = true - options?.onMouseEnter?.(polygon) - stage!.container().style.cursor = 'pointer' - onShapeMouseEnter?.(shape) - }) -} + event.cancelBubble = true; + options.onMouseEnter?.(polygon); + stage.container().style.cursor = 'pointer'; + onShapeMouseEnter?.(shape); + }); +}; export const scalePointToImage = ( point: PointerPosition, imageBoundingBox: ImageBoundingBox, -) => { - const { width, height, scale } = imageBoundingBox +): PointerPosition => { + const { width, height, scale } = imageBoundingBox; return { x: roundTo((Math.min(point.x, 1) * width) / scale, 2), y: roundTo((Math.min(point.y, 1) * height) / scale, 2), - } -} + }; +}; + +const isPoint = (coordinate: number[]): coordinate is [number, number] => + coordinate.length >= 2; const mapCoordinatesToPoints = ( - coordinates: number[][], + shape: AnnotationShape, imageBoundingBox: ImageBoundingBox, -): number[] => - coordinates.reduce((accumulator, element) => { - const { x, y } = scalePointToImage( - { x: element[0], y: element[1] }, - imageBoundingBox, - ) - accumulator = accumulator.concat([x, y]) - return accumulator - }, []) +): number[] => { + const points = shape.coordinates.filter(isPoint); + if (points.length !== shape.coordinates.length) { + console.warn( + `AnnotationShape "${shape.id}": ignored ${String(shape.coordinates.length - points.length)} coordinate(s) with fewer than 2 values`, + ); + } + return points.flatMap(([x, y]) => { + const scaled = scalePointToImage({ x, y }, imageBoundingBox); + return [scaled.x, scaled.y]; + }); +}; export const getMousePosition = ( stage: Konva.Stage | null, imageBoundingBox: ImageBoundingBox | null, -) => { +): PointerPosition | undefined => { if (!stage || !imageBoundingBox) { - return + return; } - const { x: pointerX, y: pointerY } = stage.getPointerPosition() || { + const { x: pointerX, y: pointerY } = stage.getPointerPosition() ?? { x: 0, y: 0, - } - const oldScale = stage.scaleX() - const stageX = stage.x() - const stageY = stage.y() + }; + const oldScale = stage.scaleX(); + const stageX = stage.x(); + const stageY = stage.y(); return { x: roundTo( ((pointerX - stageX) * imageBoundingBox.scale) / @@ -127,5 +141,5 @@ export const getMousePosition = ( (oldScale * imageBoundingBox.height), 2, ), - } -} + }; +}; diff --git a/src/utils/functions.ts b/src/utils/functions.ts index b2cbe8d..1b6ed94 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,56 +1,54 @@ -import Konva from 'konva' -import { Layer } from 'konva/lib/Layer' -import { LineConfig } from 'konva/lib/shapes/Line' +import Konva from 'konva'; +import type { LineConfig } from 'konva/lib/shapes/Line'; +import type { Stage } from 'konva/lib/Stage'; -import { KONVA_REFS } from '@/common/constants' +import { KonvaRefs } from '@/common/constants'; -import { Stage } from '..' - -export const drawLayer = (stage: Konva.Stage) => { - const shapesLayer = stage.findOne(`#${KONVA_REFS.shapesLayer}`) - if (shapesLayer) { - shapesLayer.batchDraw() +export const drawLayer = (stage: Konva.Stage): void => { + const shapesLayer = stage.findOne(`#${KonvaRefs.ShapesLayer}`); + if (shapesLayer instanceof Konva.Layer) { + shapesLayer.batchDraw(); } else { - console.error('drawLayer : the layer is not found') + console.error('drawLayer : the layer is not found'); } -} +}; export const drawShape = ( stage: Konva.Stage, id: string | number, config: LineConfig, -) => { - const shape = stage.findOne(`#${id}`) - if (shape) { - shape.setAttrs(config) - shape.draw() +): void => { + const shape = stage.findOne(`#${id.toString()}`); + if (shape instanceof Konva.Shape) { + shape.setAttrs(config); + shape.draw(); } else { - console.error('drawShape : The provided shape id is not valid') + console.error('drawShape : The provided shape id is not valid'); } -} +}; -export const drawShapes = (stage: Stage, config: LineConfig) => { - const shapes = stage.find(`.${KONVA_REFS.shape}`) - if (shapes.length) { +export const drawShapes = (stage: Stage, config: LineConfig): void => { + const shapes = stage.find(`.${KonvaRefs.Shape}`); + if (shapes.length > 0) { shapes.forEach((shape) => { - shape.setAttrs(config) - }) - drawLayer(stage) + shape.setAttrs(config); + }); + drawLayer(stage); } -} +}; export const setShapeConfig = ( stage: Konva.Stage, id: string | number, config: LineConfig, -) => { - const shape = stage.findOne(`#${id}`) +): void => { + const shape = stage.findOne(`#${id.toString()}`); if (shape) { - shape.setAttrs(config) + shape.setAttrs(config); } else { - console.error('setShapeConfig : The provided shape id is not valid') + console.error('setShapeConfig : The provided shape id is not valid'); } -} +}; export const toBase64 = ( base64: string, @@ -59,4 +57,4 @@ export const toBase64 = ( | 'image/jpeg' | 'image/png' | 'image/svg+xml' = 'image/jpeg', -) => `data:${type};base64,${base64}` +): string => `data:${type};base64,${base64}`; diff --git a/src/utils/getImagesFromPDF.spec.tsx b/src/utils/getImagesFromPDF.spec.tsx index 8edd2c1..d582d97 100644 --- a/src/utils/getImagesFromPDF.spec.tsx +++ b/src/utils/getImagesFromPDF.spec.tsx @@ -1,24 +1,24 @@ -import brokenPDF from 'cypress/assets/broken-pdf.pdf' -import multiPage from 'cypress/assets/multi-page.pdf' +import brokenPDF from 'cypress/assets/broken-pdf.pdf'; +import multiPage from 'cypress/assets/multi-page.pdf'; -import getImagesFromPDF from './getImagesFromPDF' +import getImagesFromPDF from './getImagesFromPDF'; describe('getImageFromPDF', () => { it('should return PDF pages', () => { getImagesFromPDF(multiPage).then((images) => { - expect(images.length).to.equal(5) - }) - }) + expect(images.length).to.equal(5); + }); + }); it('should catch error when the PDF is broken', () => { - getImagesFromPDF(brokenPDF).catch((error) => { - expect(error.name).to.equal('InvalidPDFException') - }) - }) + getImagesFromPDF(brokenPDF).catch((error: unknown) => { + expect((error as Error).name).to.equal('InvalidPDFException'); + }); + }); it('should catch error when the PDF has too many Pages', () => { - getImagesFromPDF(multiPage, 3).catch((error) => { - expect(error.name).to.equal('TooManyPagesError') - }) - }) -}) + getImagesFromPDF(multiPage, 3).catch((error: unknown) => { + expect((error as Error).name).to.equal('TooManyPagesError'); + }); + }); +}); diff --git a/src/utils/getImagesFromPDF.ts b/src/utils/getImagesFromPDF.ts index 37402b0..33a19ea 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -1,78 +1,39 @@ -import { - getDocument, - GlobalWorkerOptions, - PDFDocumentProxy, - PDFPageProxy, - version, -} from 'pdfjs-dist' -import { RenderParameters } from 'pdfjs-dist/types/src/display/api' +import { openPDF, type PDFSource } from './pdf'; -import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants' - -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js` - -const getImageFromPage = async ( - _document: PDFDocumentProxy, - pageNumber: number, - resolution = PDF_RESOLUTION, -) => { - const page: PDFPageProxy = await _document.getPage(pageNumber) - const canvas = document.createElement('canvas') - const context = canvas.getContext('2d') - const [, , width, height] = page.view - const newScale = (resolution / (height * width)) ** (1 / 2) - const safeScale = Math.min(newScale, MAX_PDF_SCALE) - const viewport = page.getViewport({ scale: safeScale }) - canvas.height = viewport.height - canvas.width = viewport.width - const renderContext = { - canvasContext: context, - viewport: viewport, - } - return page - .render(renderContext as RenderParameters) - .promise.then(() => canvas.toDataURL()) - .catch((error) => { - throw new Error(error) - }) -} - -export default function getImagesFromPDF( - file: string, +/** + * Renders every page of a PDF to a PNG data URL. Pages that fail to render + * are skipped. + * + * @deprecated Renders and holds all pages in memory at once. Use `openPDF` + * (or the `usePDFDocument` hook) to render pages progressively instead. + */ +export default async function getImagesFromPDF( + file: PDFSource, maxPages = Infinity, onSuccess?: () => void, resolution?: number, -) { - return new Promise((resolve, reject) => { - getDocument(file) - .promise.then((document: PDFDocumentProxy) => { - if (document.numPages > maxPages) { - const error = new Error('Too many pages') - error.name = 'TooManyPagesError' - reject(error) - return - } - onSuccess?.() - Promise.allSettled( - Array.from(Array(document.numPages).keys()).map((index) => - getImageFromPage(document, index + 1, resolution), - ), - ) - .then((results) => { - const images = results.reduce((accumulator, result) => { - if (result.status === 'fulfilled') { - accumulator.push(result.value) - } - return accumulator - }, []) - resolve(images) - }) - .catch((error) => { - reject(error) - }) - }) - .catch((error) => { - reject(error) - }) - }) +): Promise { + const pdf = await openPDF(file, { maxPages }); + try { + onSuccess?.(); + const pageNumbers = Array.from({ length: pdf.numPages }, (_, i) => i + 1); + const results = await Promise.allSettled( + pageNumbers.map( + async (pageNumber) => + await pdf.getPage(pageNumber, { + resolution, + output: 'data-url', + priority: 'low', + }), + ), + ); + return results + .filter( + (result): result is PromiseFulfilledResult => + result.status === 'fulfilled', + ) + .map((result) => result.value); + } finally { + await pdf.destroy(); + } } diff --git a/src/utils/getPDFPageCount.spec.tsx b/src/utils/getPDFPageCount.spec.tsx index 04f1223..fe70d16 100644 --- a/src/utils/getPDFPageCount.spec.tsx +++ b/src/utils/getPDFPageCount.spec.tsx @@ -1,18 +1,18 @@ -import brokenPDF from 'cypress/assets/broken-pdf.pdf' -import multiPage from 'cypress/assets/multi-page.pdf' +import brokenPDF from 'cypress/assets/broken-pdf.pdf'; +import multiPage from 'cypress/assets/multi-page.pdf'; -import getPDFPageCount from './getPDFPageCount' +import getPDFPageCount from './getPDFPageCount'; describe('getPDFPageCount', () => { it('should return the number of PDF pages', () => { getPDFPageCount(multiPage).then((pageCount) => { - expect(pageCount).to.equal(5) - }) - }) + expect(pageCount).to.equal(5); + }); + }); it('should catch error when the PDF is broken', () => { - getPDFPageCount(brokenPDF).catch((error) => { - expect(error.name).to.equal('InvalidPDFException') - }) - }) -}) + getPDFPageCount(brokenPDF).catch((error: unknown) => { + expect((error as Error).name).to.equal('InvalidPDFException'); + }); + }); +}); diff --git a/src/utils/getPDFPageCount.ts b/src/utils/getPDFPageCount.ts index 992edb6..ceb2bee 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,16 +1,15 @@ -import { - getDocument, - GlobalWorkerOptions, - PDFDocumentProxy, - version, -} from 'pdfjs-dist' +import { openPDF, type PDFSource } from './pdf'; -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js` - -export default function getPDFPageCount(file: string) { - return new Promise((resolve, reject) => { - getDocument(file) - .promise.then((document: PDFDocumentProxy) => resolve(document.numPages)) - .catch((error) => reject(error)) - }) +/** + * Counts the pages of a PDF. + */ +export default async function getPDFPageCount( + file: PDFSource, +): Promise { + const pdf = await openPDF(file); + try { + return pdf.numPages; + } finally { + await pdf.destroy(); + } } diff --git a/src/utils/image.spec.ts b/src/utils/image.spec.ts index 635d965..ce4f9df 100644 --- a/src/utils/image.spec.ts +++ b/src/utils/image.spec.ts @@ -1,17 +1,48 @@ -import { Stage } from 'konva/lib/Stage' +import demoHeic from 'cypress/assets/demo.heic'; +import type { Stage } from 'konva/lib/Stage'; -import { ImageBoundingBox } from '@/common/types' +import type { ImageBoundingBox } from '@/common/types'; import { computeImageBoundingBox, + prepareImage, setStageBasedImagePosition, -} from '@/utils/image' +} from '@/utils/image'; describe('image', () => { + describe('prepareImage', () => { + const withMimeType = async (type: string): Promise => { + const blob = await fetch(demoHeic).then(async (r) => await r.blob()); + return URL.createObjectURL(new Blob([blob], { type })); + }; + + [ + 'image/heic', + 'image/heif', + 'image/heif-sequence', + 'image/heic-sequence', + ].forEach((type) => { + it(`converts a ${type} blob to a JPEG data URL`, () => { + cy.then( + async () => await prepareImage(await withMimeType(type)), + ).should('match', /^data:image\/jpeg;base64,/); + }); + }); + + it('returns other image types untouched', () => { + cy.then(async () => { + const url = await withMimeType('image/png'); + return [url, await prepareImage(url)]; + }).should(([url, prepared]) => { + expect(prepared).to.equal(url); + }); + }); + }); + describe('computeImageBoundingBox', () => { it('should return the correct bounding box', () => { - const htmlDivElement = { clientWidth: 700, clientHeight: 800 } - const htmlImageElement = { width: 768, height: 1024 } + const htmlDivElement = { clientWidth: 700, clientHeight: 800 }; + const htmlImageElement = { width: 768, height: 1024 }; const expected = { x: 50, @@ -19,21 +50,21 @@ describe('image', () => { width: 600, height: 800, scale: 0.781, - } + }; const actual = computeImageBoundingBox( htmlDivElement as HTMLDivElement, htmlImageElement as HTMLImageElement, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) + expect(actual).to.deep.equal(expected); + }); + }); describe('setStageBasedImagePosition', () => { it('should set the correct bounding box', () => { - const imageBoundingBox = { x: 50, y: 0, width: 600, height: 800 } - const pointerPosition = { x: 25, y: 25 } + const imageBoundingBox = { x: 50, y: 0, width: 600, height: 800 }; + const pointerPosition = { x: 25, y: 25 }; const stage = { getAttr: () => 1, @@ -43,17 +74,17 @@ describe('image', () => { height: () => 800, position: cy.spy().as('stagePositionSpy'), batchDraw: cy.spy(), - } + }; - const expected = { x: 25, y: 0 } + const expected = { x: 25, y: 0 }; setStageBasedImagePosition({ imageBoundingBox: imageBoundingBox as ImageBoundingBox, stage: stage as unknown as Stage, newPosition: pointerPosition, - }) + }); - cy.get('@stagePositionSpy').should('be.calledWithExactly', expected) - }) - }) -}) + cy.get('@stagePositionSpy').should('be.calledWithExactly', expected); + }); + }); +}); diff --git a/src/utils/image.ts b/src/utils/image.ts index bab64f7..5c55cbc 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,174 +1,205 @@ -import heic2any from 'heic2any' -import Konva from 'konva' -import UTIF from 'utif' - -import { ImageData } from '@/common/types' - -import { Stage } from '..' -import { ImageBoundingBox, PointerPosition } from './../common/types' - -export const dataURItoBlob = (dataURI: string) => { - let byteString - const splitDataURL = dataURI.split(',') - if (splitDataURL[0].indexOf('base64') >= 0) { - // atob decodes base64 data - byteString = atob(splitDataURL[1]) +import type Konva from 'konva'; +import type { Stage } from 'konva/lib/Stage'; +import UTIF from 'utif'; + +import type { + ImageBoundingBox, + ImageData, + PointerPosition, +} from '@/common/types'; + +import { getZoomScale, setZoomScale } from '@/utils/zoom'; + +export const dataURItoBlob = (dataURI: string): Blob => { + let byteString; + const [meta, data] = dataURI.split(','); + if (meta === undefined || data === undefined) { + throw new Error('Invalid Data URI format'); + } + if (meta.includes('base64')) { + byteString = atob(data); } else { - byteString = decodeURI(dataURI.split(',')[1]) + byteString = decodeURI(data); } - const mimeString = splitDataURL[0].split(':')[1].split(';')[0] + const mimeString = meta.split(':')[1]?.split(';')[0]; // write the bytes of the string to a typed array - const ia = new Uint8Array(byteString.length) + const ia = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i) + ia[i] = byteString.charCodeAt(i); } - return new Blob([ia], { type: mimeString }) -} + return new Blob([ia], { type: mimeString }); +}; + +const HEIF_MIME_TYPES = new Set([ + 'image/heic', + 'image/heif', + 'image/heic-sequence', + 'image/heif-sequence', +]); -export const prepareImage = async (image: string) => { - const blob = await urlToBlob(image) - if (blob.type === 'image/heic') { - return await heicToJpg(blob) +export const prepareImage = async (image: string): Promise => { + const blob = await urlToBlob(image); + if (HEIF_MIME_TYPES.has(blob.type)) { + return await heicToJpg(blob); } if (blob.type === 'image/tiff') { - return await tiffToJpg(blob) + return await tiffToJpg(blob); } - return image -} + return image; +}; -export const urlToBlob = async (url: string) => +export const urlToBlob = async (url: string): Promise => await fetch(url, { method: 'GET', cache: 'no-cache', - }).then((r) => r.blob()) - -export const tiffToJpg = async (blob: Blob) => { - const arrayBuffer = await blob.arrayBuffer() - const ifds = UTIF.decode(arrayBuffer) - const firstPageOfTif = ifds[0] - UTIF.decodeImage(arrayBuffer, firstPageOfTif) - const rgba = UTIF.toRGBA8(firstPageOfTif) - - const imageWidth = firstPageOfTif.width - const imageHeight = firstPageOfTif.height - - const cnv = document.createElement('canvas') - cnv.width = imageWidth - cnv.height = imageHeight - - const ctx = cnv.getContext('2d')! - const imageData = ctx.createImageData(imageWidth, imageHeight) - for (let i = 0; i < rgba.length; i++) { - imageData.data[i] = rgba[i] + }).then(async (r) => await r.blob()); + +export const tiffToJpg = async (blob: Blob): Promise => { + const arrayBuffer = await blob.arrayBuffer(); + const ifds = UTIF.decode(arrayBuffer); + const firstPageOfTif = ifds[0]; + if (firstPageOfTif === undefined) { + throw new Error('Invalid TIFF format'); + } + UTIF.decodeImage(arrayBuffer, firstPageOfTif); + const rgba = UTIF.toRGBA8(firstPageOfTif); + + const imageWidth = firstPageOfTif.width; + const imageHeight = firstPageOfTif.height; + + const cnv = document.createElement('canvas'); + cnv.width = imageWidth; + cnv.height = imageHeight; + + const ctx = cnv.getContext('2d'); + if (!ctx) { + throw new Error('Could not get 2D context'); } - ctx.putImageData(imageData, 0, 0) - return new Promise((resolve, reject) => { - cnv.toBlob((blob) => { - if (blob) { - resolve(URL.createObjectURL(blob)) + const imageData = ctx.createImageData(imageWidth, imageHeight); + imageData.data.set(rgba); + ctx.putImageData(imageData, 0, 0); + return cnv.toDataURL('image/jpeg'); +}; + +export const heicToJpg = async (blob: Blob): Promise => { + const { default: heic2any } = await import('heic2any'); + const result = await heic2any({ blob, toType: 'image/jpeg' }); + const firstImage = Array.isArray(result) ? result[0] : result; + if (firstImage === undefined) { + throw new Error('HEIC file contains no image'); + } + return await blobToDataURL(firstImage); +}; + +const blobToDataURL = async (blob: Blob): Promise => + await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + if (typeof reader.result === 'string') { + resolve(reader.result); } else { - reject('Error getting Blob') + reject(new Error('Could not read Blob as a data URL')); } - }) - }) -} - -export const heicToJpg = async (blob: Blob) => { - const result = await heic2any({ blob }) - return URL.createObjectURL(result as Blob) -} + }; + reader.onerror = () => { + reject(new Error('Could not read Blob as a data URL')); + }; + reader.readAsDataURL(blob); + }); export const computeImageBoundingBox = ( { clientWidth, clientHeight }: HTMLDivElement, imageObj: HTMLImageElement, -) => { - const imageAspectRatio = imageObj.width / imageObj.height - const canvasAspectRatio = clientWidth / clientHeight - let renderableHeight, renderableWidth, xStart, yStart +): ImageBoundingBox => { + const imageAspectRatio = imageObj.width / imageObj.height; + const canvasAspectRatio = clientWidth / clientHeight; + let renderableHeight, renderableWidth, xStart, yStart; - xStart = 0 - yStart = 0 - renderableHeight = clientHeight - renderableWidth = clientWidth + xStart = 0; + yStart = 0; + renderableHeight = clientHeight; + renderableWidth = clientWidth; if (imageAspectRatio < canvasAspectRatio) { - renderableWidth = imageObj.width * (renderableHeight / imageObj.height) - xStart = Math.round((clientWidth - renderableWidth) / 2) + renderableWidth = imageObj.width * (renderableHeight / imageObj.height); + xStart = Math.round((clientWidth - renderableWidth) / 2); } else if (imageAspectRatio > canvasAspectRatio) { - renderableHeight = imageObj.height * (renderableWidth / imageObj.width) - yStart = Math.round((clientHeight - renderableHeight) / 2) + renderableHeight = imageObj.height * (renderableWidth / imageObj.width); + yStart = Math.round((clientHeight - renderableHeight) / 2); } return { - scale: Number(Number(renderableWidth / imageObj.width).toFixed(3)), + scale: Number( + ((renderableWidth / imageObj.width) satisfies number).toFixed(3), + ), x: xStart, y: yStart, width: Math.round(renderableWidth), height: Math.round(renderableHeight), - } -} + }; +}; -const resizeStage = (stage: Konva.Stage, container: HTMLDivElement) => { - stage.width(container.clientWidth) - stage.height(container.clientHeight) -} +const resizeStage = (stage: Konva.Stage, container: HTMLDivElement): void => { + stage.width(container.clientWidth); + stage.height(container.clientHeight); +}; export const handleResizeImage = ( stage: Konva.Stage | null, container: HTMLDivElement | null, { element, shape }: ImageData, -) => { +): ImageBoundingBox | undefined => { if (!container || !stage) { - return + return; } - resizeStage(stage, container) - const imageBoundingBox = computeImageBoundingBox(container, element) - const { x, y, width, height, scale } = imageBoundingBox - stage?.scale({ + resizeStage(stage, container); + const imageBoundingBox = computeImageBoundingBox(container, element); + const { x, y, width, height, scale } = imageBoundingBox; + stage.scale({ x: scale, y: scale, - }) - stage.setAttr('zoomScale', 1) - stage.position({ x, y }) - shape.width(width / scale) - shape.height(height / scale) - return imageBoundingBox -} + }); + setZoomScale(stage, 1); + stage.position({ x, y }); + shape.width(width / scale); + shape.height(height / scale); + return imageBoundingBox; +}; export const setStageBasedImagePosition = ({ imageBoundingBox, stage, newPosition, }: { - imageBoundingBox: ImageBoundingBox - stage: Stage - newPosition: PointerPosition -}) => { - const { x, y, width, height } = imageBoundingBox - const zoomScale = stage.getAttr('zoomScale') - let stageX = stage.x() - let stageY = stage.y() - const { x: newStageX, y: newStageY } = newPosition + imageBoundingBox: ImageBoundingBox; + stage: Stage; + newPosition: PointerPosition; +}): void => { + const { x, y, width, height } = imageBoundingBox; + const zoomScale = getZoomScale(stage); + let stageX = stage.x(); + let stageY = stage.y(); + const { x: newStageX, y: newStageY } = newPosition; if ( (newStageX < x * zoomScale || newStageX < stageX) && (newStageX > stage.width() - (width + x) * zoomScale || newStageX > stageX) ) { - stageX = newStageX + stageX = newStageX; } if ( (newStageY < y * zoomScale || newStageY < stageY) && (newStageY > stage.height() - (height + y) * zoomScale || newStageY > stageY) ) { - stageY = newStageY + stageY = newStageY; } stage.position({ x: stageX, y: stageY, - }) - stage.batchDraw() -} + }); + stage.batchDraw(); +}; diff --git a/src/utils/layer.ts b/src/utils/layer.ts index cdad1f0..efc8f99 100644 --- a/src/utils/layer.ts +++ b/src/utils/layer.ts @@ -1,7 +1,7 @@ -import { AnnotationLayers } from '@/common/types' +import type { AnnotationLayers } from '@/common/types'; -export const clearLayers = (layers: AnnotationLayers) => { - layers.shapes.destroyChildren() - layers.image.batchDraw() - layers.shapes.batchDraw() -} +export const clearLayers = (layers: AnnotationLayers): void => { + layers.shapes.destroyChildren(); + layers.image.batchDraw(); + layers.shapes.batchDraw(); +}; diff --git a/src/utils/orientation.ts b/src/utils/orientation.ts index fcbe5ea..45db711 100644 --- a/src/utils/orientation.ts +++ b/src/utils/orientation.ts @@ -1,98 +1,104 @@ -import { AnnotationData } from '@/common/types' +import type { Orientation } from '@/common/types'; -export const rotateImage = ({ - image, - orientation: degrees, -}: AnnotationData) => { - let orientation = 1 - switch (degrees) { - case 0: - orientation = 1 - break - case 90: - orientation = 6 - break - case 180: - orientation = 3 - break - case 270: - orientation = 8 - break - default: - break +const EXIF_ORIENTATION: Record = { + 0: 1, + 90: 6, + 180: 3, + 270: 8, +}; + +export const rotateImage = async ( + image: string, + degrees: Orientation = 0, +): Promise => { + if (degrees === 0) { + return image; } - return applyRotation(image as string, orientation) -} + return await applyRotation(image, EXIF_ORIENTATION[degrees]); +}; -const applyRotation = (file: string, orientation: number) => - new Promise((resolve) => { - const image = new Image() - image.crossOrigin = '' +const applyRotation = async ( + file: string, + orientation: number, +): Promise => + await new Promise((resolve, reject) => { + const image = new Image(); + image.crossOrigin = ''; + image.onerror = () => { + reject(new Error('Failed to load image for rotation')); + }; image.onload = () => { - // Create canvas (off screen) to render image and apply transformations - const canvas = document.createElement('canvas') + const canvas = document.createElement('canvas'); const context = canvas.getContext('2d', { alpha: false, - }) as CanvasRenderingContext2D - const { width, height } = image + }); + if (!context) { + reject(new Error('Could not acquire a 2D canvas context')); + return; + } + const { width, height } = image; const [outputWidth, outputHeight] = - orientation > 4 && orientation < 9 ? [height, width] : [width, height] + orientation > 4 && orientation < 9 ? [height, width] : [width, height]; // Using a square canvas on max(width, height) to rotate rectangular images - const wh = Math.max(width, height) - canvas.width = wh - canvas.height = wh + const wh = Math.max(width, height); + canvas.width = wh; + canvas.height = wh; // output image can be aligned to the right or bottom - let rightAligned = false - let bottomAligned = false + let rightAligned = false; + let bottomAligned = false; switch (orientation) { case 2: - context.transform(-1, 0, 0, 1, wh, 0) - rightAligned = true - break + context.transform(-1, 0, 0, 1, wh, 0); + rightAligned = true; + break; case 3: - context.transform(-1, 0, 0, -1, wh, wh) - rightAligned = true - bottomAligned = true - break + context.transform(-1, 0, 0, -1, wh, wh); + rightAligned = true; + bottomAligned = true; + break; case 4: - context.transform(1, 0, 0, -1, 0, wh) - bottomAligned = true - break + context.transform(1, 0, 0, -1, 0, wh); + bottomAligned = true; + break; case 5: - context.transform(0, 1, 1, 0, 0, 0) - break + context.transform(0, 1, 1, 0, 0, 0); + break; case 6: - context.transform(0, 1, -1, 0, wh, 0) - rightAligned = true - break + context.transform(0, 1, -1, 0, wh, 0); + rightAligned = true; + break; case 7: - context.transform(0, -1, -1, 0, wh, wh) - rightAligned = true - bottomAligned = true - break + context.transform(0, -1, -1, 0, wh, wh); + rightAligned = true; + bottomAligned = true; + break; case 8: - context.transform(0, -1, 1, 0, 0, wh) - bottomAligned = true - break + context.transform(0, -1, 1, 0, 0, wh); + bottomAligned = true; + break; default: - break + break; } // draw image now that transform matrix is set - context.drawImage(image, 0, 0, width, height) + context.drawImage(image, 0, 0, width, height); // transfer image to new canvas with correct dimensions - const outputCanvas = document.createElement('canvas') - outputCanvas.width = outputWidth - outputCanvas.height = outputHeight - const outputContext = outputCanvas.getContext('2d', { alpha: false }) - const sx = rightAligned ? canvas.width - outputCanvas.width : 0 - const sy = bottomAligned ? canvas.height - outputCanvas.height : 0 - outputContext!.drawImage( + const outputCanvas = document.createElement('canvas'); + outputCanvas.width = outputWidth; + outputCanvas.height = outputHeight; + const outputContext = outputCanvas.getContext('2d', { alpha: false }); + if (!outputContext) { + reject(new Error('Could not acquire a 2D canvas context')); + return; + } + const sx = rightAligned ? canvas.width - outputCanvas.width : 0; + const sy = bottomAligned ? canvas.height - outputCanvas.height : 0; + outputContext.drawImage( canvas, sx, sy, @@ -102,11 +108,11 @@ const applyRotation = (file: string, orientation: number) => 0, outputCanvas.width, outputCanvas.height, - ) + ); // export image data - resolve(outputCanvas.toDataURL('image/jpeg')) - } + resolve(outputCanvas.toDataURL('image/jpeg')); + }; - image.src = file - }) + image.src = file; + }); diff --git a/src/utils/pdf.spec.ts b/src/utils/pdf.spec.ts new file mode 100644 index 0000000..87e1b04 --- /dev/null +++ b/src/utils/pdf.spec.ts @@ -0,0 +1,415 @@ +import brokenPDF from 'cypress/assets/broken-pdf.pdf'; +import multiPage from 'cypress/assets/multi-page.pdf'; + +import { openPDF, type PDFDocumentHandle, type PDFRenderedPage } from './pdf'; + +const PAGE_COUNT = 5; + +const collect = async ( + iterable: AsyncIterable, +): Promise => { + const pages: PDFRenderedPage[] = []; + for await (const page of iterable) { + pages.push(page); + } + return pages; +}; + +const run = (task: () => Promise): Cypress.Chainable => + cy.then(async () => await task()); + +const errorName = async (promise: Promise): Promise => { + try { + await promise; + return 'resolved'; + } catch (error: unknown) { + return (error as Error).name; + } +}; + +describe('openPDF', () => { + let handle: PDFDocumentHandle | undefined; + + afterEach(() => { + if (handle !== undefined) { + cy.wrap(handle.destroy()); + handle = undefined; + } + }); + + it('exposes the page count without rendering anything', () => { + cy.wrap(openPDF(multiPage)).then((opened) => { + handle = opened as PDFDocumentHandle; + expect(handle.numPages).to.equal(PAGE_COUNT); + }); + }); + + it('accepts a Blob source', () => { + cy.wrap( + fetch(multiPage) + .then(async (response) => await response.blob()) + .then(async (blob) => await openPDF(blob)), + ).then((opened) => { + handle = opened as PDFDocumentHandle; + expect(handle.numPages).to.equal(PAGE_COUNT); + }); + }); + + it('leaves a caller-owned byte source intact and reusable', () => { + run(async () => { + const buffer: ArrayBuffer = await fetch(multiPage).then( + async (r) => await r.arrayBuffer(), + ); + const bytes = new Uint8Array(buffer); + const length = bytes.byteLength; + const first = await openPDF(bytes); + await first.destroy(); + const second = await openPDF(bytes.buffer); + handle = second; + return { length, after: bytes.byteLength, numPages: second.numPages }; + }).should(({ length, after, numPages }) => { + expect(length).to.be.greaterThan(0); + expect(after).to.equal(length); + expect(numPages).to.equal(PAGE_COUNT); + }); + }); + + it('rejects an invalid resolution with a RangeError', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await Promise.all( + [0, -1, Number.NaN, Number.POSITIVE_INFINITY].map( + async (resolution) => + await errorName(opened.getPage(1, { resolution })), + ), + ); + }).should((names) => { + expect(names).to.deep.equal(Array(4).fill('RangeError')); + }); + }); + + it('still produces an image for a tiny resolution', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await opened.getPage(1, { resolution: 1, output: 'data-url' }); + }).should('match', /^data:image\/png;base64,/); + }); + + it('renders close to the requested pixel resolution', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const image = await opened.getPage(1, { resolution: 300_000 }); + return await new Promise((resolve, reject) => { + const element = new Image(); + element.onload = () => { + resolve(element.naturalWidth * element.naturalHeight); + }; + element.onerror = () => { + reject(new Error('undecodable page')); + }; + element.src = image; + }); + }).should((area) => { + expect(area).to.be.within(300_000 * 0.9, 300_000 * 1.1); + }); + }); + + it('renders a page as an object URL by default and as a data URL on request', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await Promise.all([ + opened.getPage(1), + opened.getPage(1, { output: 'data-url' }), + ]); + }).should(([objectUrl, dataUrl]) => { + expect(objectUrl).to.match(/^blob:/); + expect(dataUrl).to.match(/^data:image\/png;base64,/); + }); + }); + + it('returns the cached image when the same page is requested again', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const first = await opened.getPage(2); + const second = await opened.getPage(2); + const other = await opened.getPage(2, { resolution: 50_000 }); + return { first, second, other }; + }).should(({ first, second, other }) => { + expect(second).to.equal(first); + expect(other).to.not.equal(first); + }); + }); + + it('rejects out of range page numbers', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await Promise.all([ + errorName(opened.getPage(0)), + errorName(opened.getPage(PAGE_COUNT + 1)), + ]); + }).should('deep.equal', ['RangeError', 'RangeError']); + }); + + it('rejects an invalid range as a whole instead of rendering the valid part', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const rejected = await Promise.all([ + errorName(collect(opened.getPages(0, PAGE_COUNT))), + errorName(collect(opened.getPages(1, PAGE_COUNT + 1))), + errorName(collect(opened.getPages(1, Number.MAX_SAFE_INTEGER))), + errorName(collect(opened.getPages(3, 2))), + ]); + return rejected; + }).should( + 'deep.equal', + Array.from({ length: 4 }, () => 'RangeError'), + ); + }); + + it('yields an inclusive 1-based range in order', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await collect(opened.getPages(2, 4)); + }).should((pages) => { + expect(pages.map((page) => page.pageNumber)).to.deep.equal([2, 3, 4]); + pages.forEach((page) => { + expect(page.image).to.match(/^blob:/); + }); + }); + }); + + it('renders high priority requests before queued low priority ones', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const order: number[] = []; + const track = async ( + pageNumber: number, + priority: 'high' | 'low', + ): Promise => { + await opened.getPage(pageNumber, { priority }); + order.push(pageNumber); + }; + await Promise.all([ + track(1, 'low'), + track(2, 'low'), + track(3, 'low'), + track(5, 'high'), + ]); + return order; + }).should((order) => { + expect(order[0]).to.equal(1); + expect(order[1]).to.equal(5); + expect(order.slice(2)).to.deep.equal([2, 3]); + }); + }); + + it('rejects opening with an AbortError when the signal fires while loading', () => { + run(async () => { + const controller = new AbortController(); + const opening = openPDF(multiPage, { signal: controller.signal }); + controller.abort(); + const name = await errorName(opening); + // The shared worker stays usable for the next document. + const opened = await openPDF(multiPage); + handle = opened; + return { name, numPages: opened.numPages }; + }).should(({ name, numPages }) => { + expect(name).to.equal('AbortError'); + expect(numPages).to.equal(PAGE_COUNT); + }); + }); + + it('rejects opening immediately with an already aborted signal', () => { + run(async () => { + const controller = new AbortController(); + controller.abort(); + return await errorName(openPDF(multiPage, { signal: controller.signal })); + }).should('equal', 'AbortError'); + }); + + it('rejects with an AbortError when a queued request is aborted', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const controller = new AbortController(); + const first = opened.getPage(1); + const aborted = errorName( + opened.getPage(2, { signal: controller.signal }), + ); + controller.abort(); + await first; + return await aborted; + }).should('equal', 'AbortError'); + }); + + it('lets one caller abort without affecting another waiting on the same page', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const controller = new AbortController(); + const blocker = opened.getPage(1); + const kept = opened.getPage(3); + const aborted = errorName( + opened.getPage(3, { signal: controller.signal }), + ); + controller.abort(); + await blocker; + return { aborted: await aborted, kept: await kept }; + }).should(({ aborted, kept }) => { + expect(aborted).to.equal('AbortError'); + expect(kept).to.match(/^blob:/); + }); + }); + + it('cancels a shared render only once every caller has aborted', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const first = new AbortController(); + const second = new AbortController(); + const blocker = opened.getPage(1); + const a = errorName(opened.getPage(4, { signal: first.signal })); + const b = errorName(opened.getPage(4, { signal: second.signal })); + first.abort(); + second.abort(); + await blocker; + const fresh = opened.getPage(4); + return { a: await a, b: await b, fresh: await fresh }; + }).should(({ a, b, fresh }) => { + expect(a).to.equal('AbortError'); + expect(b).to.equal('AbortError'); + expect(fresh).to.match(/^blob:/); + }); + }); + + it('starts a fresh render when a page is requested right after its only caller aborted mid-render', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const controller = new AbortController(); + const first = opened.getPage(1); + const aborted = errorName( + opened.getPage(2, { signal: controller.signal }), + ); + await first; + controller.abort(); + const retried = opened.getPage(2); + return { aborted: await aborted, retried: await retried }; + }).should(({ aborted, retried }) => { + expect(aborted).to.equal('AbortError'); + expect(retried).to.match(/^blob:/); + }); + }); + + it('promotes an already queued low priority page when requested again with high priority', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + const order: number[] = []; + const track = async (promise: Promise, page: number) => { + await promise; + order.push(page); + }; + const tasks = [ + track(opened.getPage(1, { priority: 'low' }), 1), + track(opened.getPage(2, { priority: 'low' }), 2), + track(opened.getPage(3, { priority: 'low' }), 3), + track(opened.getPage(4, { priority: 'low' }), 4), + ]; + tasks.push(track(opened.getPage(4, { priority: 'high' }), 44)); + await Promise.all(tasks); + return order; + }).should((order) => { + expect(order.slice(0, 2)).to.deep.equal([1, 4]); + }); + }); + + it('rejects immediately when the signal is already aborted', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + return await errorName( + opened.getPage(1, { signal: AbortSignal.abort() }), + ); + }).should('equal', 'AbortError'); + }); + + it('revokes object URLs and refuses further renders once destroyed', () => { + run(async () => { + const opened = await openPDF(multiPage); + const image = await opened.getPage(1); + await opened.destroy(); + const revoked = await fetch(image).then( + () => false, + () => true, + ); + return { revoked, afterDestroy: await errorName(opened.getPage(2)) }; + }).should(({ revoked, afterDestroy }) => { + expect(revoked).to.equal(true); + expect(afterDestroy).to.not.equal('resolved'); + }); + }); + + it('rejects a render that completes after destroy instead of resolving it', () => { + run(async () => { + const opened = await openPDF(multiPage); + const pending = errorName(opened.getPage(1)); + await opened.destroy(); + return await pending; + }).should('not.equal', 'resolved'); + }); + + it('renders getPages lazily so stopping early leaves nothing queued', () => { + run(async () => { + const opened = await openPDF(multiPage); + handle = opened; + for await (const page of opened.getPages(1, PAGE_COUNT)) { + if (page.pageNumber === 1) { + break; + } + } + const order: number[] = []; + await Promise.all( + [5, 2].map(async (pageNumber) => { + await opened.getPage(pageNumber, { priority: 'low' }); + order.push(pageNumber); + }), + ); + return order; + }).should('deep.equal', [5, 2]); + }); + + it('opens a new document while the previous one is still being destroyed', () => { + run(async () => { + const previous = await openPDF(multiPage); + const destroying = previous.destroy(); + const next = await openPDF(multiPage); + handle = next; + await destroying; + return next.numPages; + }).should('equal', PAGE_COUNT); + }); + + it('rejects documents above maxPages with a TooManyPagesError', () => { + cy.wrap(errorName(openPDF(multiPage, { maxPages: 3 }))).should( + 'equal', + 'TooManyPagesError', + ); + }); + + it('rejects a broken PDF with an InvalidPDFException', () => { + cy.wrap(errorName(openPDF(brokenPDF))).should( + 'equal', + 'InvalidPDFException', + ); + }); +}); diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts new file mode 100644 index 0000000..1765de6 --- /dev/null +++ b/src/utils/pdf.ts @@ -0,0 +1,503 @@ +import type * as Pdfjs from 'pdfjs-dist'; +import type { + PDFDocumentProxy, + PDFPageProxy, + PDFWorker, + RenderTask, +} from 'pdfjs-dist'; + +import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants'; + +type PdfjsRuntime = { pdfjs: typeof Pdfjs; worker: PDFWorker }; + +let runtime: Promise | undefined; + +/** + * Loads pdf.js and its worker on first use, so consumers need no CDN access or + * manual worker configuration. Both live in lazy chunks outside the main bundle. + * + * The worker is wrapped in one long-lived `PDFWorker` handed explicitly to + * every `getDocument` call. Documents therefore never own the worker: pdf.js + * only flags a worker as "being destroyed" (making the next `getDocument` + * throw until the destroy settles) when it created the wrapper itself, which + * would otherwise break opening a document right after closing another. + */ +const loadPdfjs = async (): Promise => { + runtime ??= Promise.all([import('pdfjs-dist'), import('./pdfWorker')]) + .then(([pdfjs, { createPdfWorker }]) => ({ + pdfjs, + worker: pdfjs.PDFWorker.create({ port: createPdfWorker() }), + })) + .catch((error: unknown) => { + runtime = undefined; + throw error; + }); + return await runtime; +}; + +export type PDFSource = string | Blob | ArrayBuffer | Uint8Array; + +export type PDFPageOutput = 'object-url' | 'data-url'; + +export type PDFPagePriority = 'high' | 'low'; + +export type PDFPageOptions = { + /** + * Target pixel count of the rendered page (width × height). Defaults to + * 1.5 Mpx, which is what the viewer displays; use a much lower value such + * as 50 000 for thumbnails. Renders at different resolutions are cached + * independently. Must be a finite positive number. + */ + resolution?: number; + /** + * `object-url` (default) returns a `blob:` URL backed by a PNG blob, which + * is cheaper to hold in memory and is revoked by `destroy()`. `data-url` + * returns a base64 PNG data URL for callers that need a self-contained + * string. + */ + output?: PDFPageOutput; + /** + * `high` (default for `getPage`) puts the request ahead of everything still + * waiting, so that a page the user explicitly asked for is rendered before + * background prefetches. `low` (default for `getPages`) appends to the + * queue. + */ + priority?: PDFPagePriority; + /** + * Aborting removes the request from the queue, or cancels the in-progress + * render, and rejects the returned promise with an `AbortError`. + */ + signal?: AbortSignal; +}; + +export type PDFRenderedPage = { + /** 1-based page number. */ + pageNumber: number; + image: string; +}; + +/** + * A parsed PDF document from which pages can be rendered on demand. Pages + * render one at a time (pdf.js renders serially per document anyway), in + * priority order, and every successful render is cached for the lifetime of + * the handle. + */ +export type PDFDocumentHandle = { + readonly numPages: number; + /** Renders a single page. `pageNumber` is 1-based, like pdf.js. */ + getPage: (pageNumber: number, options?: PDFPageOptions) => Promise; + /** + * Renders pages `from` to `to` inclusive (1-based) one after the other, + * yielding each page as soon as it is ready. Iterate with `for await`; + * stopping early leaves no pending render behind. + */ + getPages: ( + from: number, + to: number, + options?: PDFPageOptions, + ) => AsyncIterable; + /** + * Cancels pending renders, revokes every object URL handed out by this + * handle and releases the pdf.js document. The handle is unusable after. + */ + destroy: () => Promise; +}; + +export type OpenPDFOptions = { + /** Rejects with a `TooManyPagesError` when the document has more pages. */ + maxPages?: number; + /** + * Aborting stops the download/parse, releases the partial document and + * rejects with an `AbortError`. + */ + signal?: AbortSignal; +}; + +/** One awaiting caller of a render; several callers can share a request. */ +type Caller = { + resolve: (image: string) => void; + reject: (error: unknown) => void; + signal: AbortSignal | undefined; + onAbort: (() => void) | undefined; +}; + +/** + * A page render shared by every caller that asked for the same page at the + * same resolution and output. It is cancelled only once all of them aborted. + */ +type RenderRequest = { + key: string; + pageNumber: number; + resolution: number; + output: PDFPageOutput; + callers: Caller[]; +}; + +const abortError = (): Error => { + const error = new Error('PDF page rendering was aborted'); + error.name = 'AbortError'; + return error; +}; + +const tooManyPagesError = (): Error => { + const error = new Error('Too many pages'); + error.name = 'TooManyPagesError'; + return error; +}; + +const destroyedError = (): Error => + new Error('This PDF document handle has been destroyed'); + +const assertResolution = (resolution: number): void => { + if (!Number.isFinite(resolution) || resolution <= 0) { + throw new RangeError( + `resolution must be a positive number, got ${String(resolution)}`, + ); + } +}; + +const assertPageInRange = (pageNumber: number, numPages: number): void => { + if ( + !Number.isInteger(pageNumber) || + pageNumber < 1 || + pageNumber > numPages + ) { + throw new RangeError( + `Page ${String(pageNumber)} is out of range (1-${String(numPages)})`, + ); + } +}; + +/** + * pdf.js transfers `data` to its worker and takes ownership of the buffer, so + * caller-owned bytes are copied first to keep the source reusable. + */ +const toDocumentParameters = async ( + source: PDFSource, +): Promise<{ url: string } | { data: Uint8Array }> => { + if (typeof source === 'string') { + return { url: source }; + } + if (source instanceof Blob) { + return { data: new Uint8Array(await source.arrayBuffer()) }; + } + if (source instanceof Uint8Array) { + return { data: source.slice() }; + } + return { data: new Uint8Array(source.slice(0)) }; +}; + +const canvasToBlob = async (canvas: HTMLCanvasElement): Promise => + await new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (blob === null) { + reject(new Error('Could not encode the rendered PDF page')); + } else { + resolve(blob); + } + }, 'image/png'); + }); + +/** + * Serial, priority-ordered render queue bound to one pdf.js document. Only + * one render task is in flight at any time, so a high-priority request can + * only be delayed by the single page currently being drawn. + */ +class PDFRenderQueue { + private readonly queue: RenderRequest[] = []; + private readonly cache = new Map(); + private readonly inFlight = new Map(); + private readonly objectUrls = new Set(); + private current: { request: RenderRequest; task?: RenderTask } | undefined; + private destroyed = false; + + constructor(private readonly document: PDFDocumentProxy) {} + + public async request( + pageNumber: number, + options: PDFPageOptions, + defaultPriority: PDFPagePriority, + ): Promise { + if (this.destroyed) { + throw destroyedError(); + } + assertPageInRange(pageNumber, this.document.numPages); + const resolution = options.resolution ?? PDF_RESOLUTION; + assertResolution(resolution); + if (options.signal?.aborted === true) { + throw abortError(); + } + + const output = options.output ?? 'object-url'; + const key = `${String(pageNumber)}@${String(resolution)}:${output}`; + + const cached = this.cache.get(key); + if (cached !== undefined) { + return cached; + } + + const priority = options.priority ?? defaultPriority; + let request = this.inFlight.get(key); + if (request === undefined) { + request = { key, pageNumber, resolution, output, callers: [] }; + this.inFlight.set(key, request); + this.queue.push(request); + } + if (priority === 'high') { + this.promote(request); + } + const image = this.attach(request, options.signal); + void this.drain(); + return await image; + } + + public async destroy(): Promise { + if (this.destroyed) { + return; + } + this.destroyed = true; + this.queue.splice(0).forEach((request) => { + this.settle(request, (caller) => { + caller.reject(destroyedError()); + }); + }); + this.current?.task?.cancel(); + this.objectUrls.forEach((url) => { + URL.revokeObjectURL(url); + }); + this.objectUrls.clear(); + this.cache.clear(); + await this.document.loadingTask.destroy(); + } + + /** Moves a queued request to the front; no-op if it is already rendering. */ + private promote(request: RenderRequest): void { + const index = this.queue.indexOf(request); + if (index > 0) { + this.queue.splice(index, 1); + this.queue.unshift(request); + } + } + + /** + * Registers a caller on a request and returns its own promise, which + * rejects with an `AbortError` as soon as *its* signal aborts without + * affecting the other callers. + */ + private async attach( + request: RenderRequest, + signal: AbortSignal | undefined, + ): Promise { + return await new Promise((resolve, reject) => { + const caller: Caller = { resolve, reject, signal, onAbort: undefined }; + if (signal !== undefined) { + caller.onAbort = () => { + this.detach(request, caller); + }; + signal.addEventListener('abort', caller.onAbort, { once: true }); + } + request.callers.push(caller); + }); + } + + /** + * Removes an aborting caller. The shared render is dropped (or cancelled + * if in progress) only when nobody is waiting for it anymore, and leaves + * deduplication immediately so a new request for the same page starts a + * fresh render instead of joining the cancelled one. + */ + private detach(request: RenderRequest, caller: Caller): void { + const index = request.callers.indexOf(caller); + if (index === -1) { + return; + } + request.callers.splice(index, 1); + caller.reject(abortError()); + if (request.callers.length > 0) { + return; + } + const queued = this.queue.indexOf(request); + if (queued !== -1) { + this.queue.splice(queued, 1); + this.inFlight.delete(request.key); + return; + } + if (this.current?.request === request) { + this.inFlight.delete(request.key); + this.current.task?.cancel(); + } + } + + /** Releases an image produced after `destroy()` so it does not leak. */ + private discard(request: RenderRequest, image: string): void { + if (request.output === 'object-url') { + this.objectUrls.delete(image); + URL.revokeObjectURL(image); + } + } + + /** Settles every remaining caller and releases the request. */ + private settle( + request: RenderRequest, + settleCaller: (caller: Caller) => void, + ): void { + if (this.inFlight.get(request.key) === request) { + this.inFlight.delete(request.key); + } + request.callers.splice(0).forEach((caller) => { + if (caller.signal !== undefined && caller.onAbort !== undefined) { + caller.signal.removeEventListener('abort', caller.onAbort); + } + settleCaller(caller); + }); + } + + private async drain(): Promise { + if (this.current !== undefined) { + return; + } + let request = this.queue.shift(); + while (request !== undefined) { + this.current = { request }; + try { + const image = await this.render(request); + if (this.destroyed) { + this.discard(request, image); + throw destroyedError(); + } + this.cache.set(request.key, image); + this.settle(request, (caller) => { + caller.resolve(image); + }); + } catch (error: unknown) { + const reason = this.destroyed ? destroyedError() : error; + this.settle(request, (caller) => { + caller.reject(reason); + }); + } finally { + this.current = undefined; + } + request = this.queue.shift(); + } + } + + private async render(request: RenderRequest): Promise { + const page = await this.document.getPage(request.pageNumber); + try { + return await this.paint(page, request); + } finally { + // The encoded image is cached by the queue; drop pdf.js's own + // operator list and decoded resources for this page. + page.cleanup(); + } + } + + private async paint( + page: PDFPageProxy, + request: RenderRequest, + ): Promise { + const { width, height } = page.getViewport({ scale: 1 }); + const scale = Math.min( + (request.resolution / (height * width)) ** (1 / 2), + MAX_PDF_SCALE, + ); + const viewport = page.getViewport({ scale }); + const canvas = document.createElement('canvas'); + canvas.width = Math.max(1, Math.floor(viewport.width)); + canvas.height = Math.max(1, Math.floor(viewport.height)); + + const task = page.render({ canvas, viewport }); + if (this.current?.request === request) { + this.current.task = task; + } + if (request.callers.length === 0 || this.destroyed) { + task.cancel(); + } + await task.promise; + + if (request.output === 'data-url') { + return canvas.toDataURL('image/png'); + } + const url = URL.createObjectURL(await canvasToBlob(canvas)); + this.objectUrls.add(url); + return url; + } +} + +/** + * Parses a PDF once and returns a handle to render its pages on demand. + * Prefer this over `getImagesFromPDF` whenever the document may have more + * than a handful of pages: only the pages you ask for are rendered, the + * requested one first, and memory is released by `destroy()`. + * + * @example + * const doc = await openPDF(file); + * const firstPage = await doc.getPage(1); + * for await (const { pageNumber, image } of doc.getPages(2, 5)) { + * show(pageNumber, image); + * } + * await doc.destroy(); + */ +export const openPDF = async ( + source: PDFSource, + options: OpenPDFOptions = {}, +): Promise => { + const { signal } = options; + const isAborted = (): boolean => signal?.aborted === true; + if (isAborted()) { + throw abortError(); + } + const { pdfjs, worker } = await loadPdfjs(); + const parameters = await toDocumentParameters(source); + if (isAborted()) { + throw abortError(); + } + const loadingTask = pdfjs.getDocument({ ...parameters, worker }); + const onAbort = (): void => { + void loadingTask.destroy(); + }; + signal?.addEventListener('abort', onAbort, { once: true }); + let pdf: PDFDocumentProxy; + try { + pdf = await loadingTask.promise; + } catch (error: unknown) { + throw isAborted() ? abortError() : error; + } finally { + signal?.removeEventListener('abort', onAbort); + } + if (isAborted()) { + await loadingTask.destroy(); + throw abortError(); + } + if (options.maxPages !== undefined && pdf.numPages > options.maxPages) { + await loadingTask.destroy(); + throw tooManyPagesError(); + } + + const queue = new PDFRenderQueue(pdf); + return { + numPages: pdf.numPages, + getPage: async (pageNumber, pageOptions = {}) => + await queue.request(pageNumber, pageOptions, 'high'), + getPages: (from, to, pageOptions = {}) => ({ + async *[Symbol.asyncIterator]() { + assertPageInRange(from, pdf.numPages); + assertPageInRange(to, pdf.numPages); + if (to < from) { + throw new RangeError( + `Invalid page range: ${String(from)}-${String(to)}`, + ); + } + for (let pageNumber = from; pageNumber <= to; pageNumber += 1) { + yield { + pageNumber, + image: await queue.request(pageNumber, pageOptions, 'low'), + }; + } + }, + }), + destroy: async () => { + await queue.destroy(); + }, + }; +}; diff --git a/src/utils/pdfWorker.ts b/src/utils/pdfWorker.ts new file mode 100644 index 0000000..df8a869 --- /dev/null +++ b/src/utils/pdfWorker.ts @@ -0,0 +1,3 @@ +import PdfWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker&inline'; + +export const createPdfWorker = (): Worker => new PdfWorker(); diff --git a/src/utils/roundTo.spec.ts b/src/utils/roundTo.spec.ts index 424c0f9..b663de6 100644 --- a/src/utils/roundTo.spec.ts +++ b/src/utils/roundTo.spec.ts @@ -1,33 +1,33 @@ -import { roundTo } from '@/utils/roundTo' +import { roundTo } from '@/utils/roundTo'; describe('roundTo', () => { it('should round to the correct precision', () => { - const value = 1.23456789 - const precision = 2 + const value = 1.23456789; + const precision = 2; - const expected = 1.23 - const actual = roundTo(value, precision) + const expected = 1.23; + const actual = roundTo(value, precision); - expect(actual).to.equal(expected) - }) + expect(actual).to.equal(expected); + }); it('should round to the correct precision with negative number', () => { - const value = -1.23456789 - const precision = 2 + const value = -1.23456789; + const precision = 2; - const expected = -1.23 - const actual = roundTo(value, precision) + const expected = -1.23; + const actual = roundTo(value, precision); - expect(actual).to.equal(expected) - }) + expect(actual).to.equal(expected); + }); it('should round to the correct precision with integer', () => { - const value = 1.5 - const precision = 0 + const value = 1.5; + const precision = 0; - const expected = 2 - const actual = roundTo(value, precision) + const expected = 2; + const actual = roundTo(value, precision); - expect(actual).to.equal(expected) - }) -}) + expect(actual).to.equal(expected); + }); +}); diff --git a/src/utils/roundTo.ts b/src/utils/roundTo.ts index 328f4ea..ea02d99 100644 --- a/src/utils/roundTo.ts +++ b/src/utils/roundTo.ts @@ -1,5 +1,5 @@ -export const roundTo = (value: number, precision = 0) => { - const multiplier = Math.pow(10, precision) - const newValue = parseFloat((Math.abs(value) * multiplier).toFixed(11)) - return (Math.round(newValue) / multiplier) * Math.sign(value) -} +export const roundTo = (value: number, precision = 0): number => { + const multiplier = Math.pow(10, precision); + const newValue = parseFloat((Math.abs(value) * multiplier).toFixed(11)); + return (Math.round(newValue) / multiplier) * Math.sign(value); +}; diff --git a/src/utils/selection.spec.ts b/src/utils/selection.spec.ts index fa11062..1c310cc 100644 --- a/src/utils/selection.spec.ts +++ b/src/utils/selection.spec.ts @@ -1,6 +1,6 @@ -import { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; -import { calculateSelectionPoint } from '@/utils/selection' +import { calculateSelectionPoint } from '@/utils/selection'; describe('selection', () => { describe('calculateSelectionPoint', () => { @@ -9,25 +9,25 @@ describe('selection', () => { position: () => ({ x: 50, y: 0 }), getPointerPosition: () => ({ x: 2, y: 2 }), scaleX: () => 0.781, - } + }; - const expected = { x: -61.46, y: 2.56 } - const actual = calculateSelectionPoint(stage as Stage) + const expected = { x: -61.46, y: 2.56 }; + const actual = calculateSelectionPoint(stage as Stage); - expect(actual).to.deep.equal(expected) - }) + expect(actual).to.deep.equal(expected); + }); it('should return undefined if pointerPosition is null', () => { const stage = { position: () => ({ x: 50, y: 0 }), getPointerPosition: () => null, scaleX: () => 0.781, - } + }; - const expected = undefined - const actual = calculateSelectionPoint(stage as Stage) + const expected = undefined; + const actual = calculateSelectionPoint(stage as Stage); - expect(actual).to.deep.equal(expected) - }) - }) -}) + expect(actual).to.deep.equal(expected); + }); + }); +}); diff --git a/src/utils/selection.ts b/src/utils/selection.ts index 80e35b9..3e4b234 100644 --- a/src/utils/selection.ts +++ b/src/utils/selection.ts @@ -1,102 +1,133 @@ -import Konva from 'konva' -import { Layer } from 'konva/lib/Layer' -import { KonvaEventObject } from 'konva/lib/Node' -import { Line } from 'konva/lib/shapes/Line' -import { Rect } from 'konva/lib/shapes/Rect' -import { Stage } from 'konva/lib/Stage' +import Konva from 'konva'; +import type { Layer } from 'konva/lib/Layer'; +import type { KonvaEventObject } from 'konva/lib/Node'; +import type { Rect } from 'konva/lib/shapes/Rect'; +import type { Stage } from 'konva/lib/Stage'; -import { KONVA_REFS } from '@/common/constants' -import { AnnotationShape, AnnotationViewerOptions } from '@/common/types' +import { KonvaRefs } from '@/common/constants'; +import type { + AnnotationShape, + AnnotationViewerOptions, + PointerPosition, +} from '@/common/types'; -import { roundTo } from './roundTo' +import { getShapeFromNode } from '@/utils/canvas'; -export const createSelectionRect = (options: AnnotationViewerOptions) => +import { roundTo } from './roundTo'; + +const selectionAnchor = new WeakMap(); + +export const createSelectionRect = (options: AnnotationViewerOptions): Rect => new Konva.Rect({ visible: false, ...options.selectionRectConfig, - }) + }); -export const calculateSelectionPoint = (stage: Stage) => { - const stagePosition = stage.position() - const pointerPosition = stage.getPointerPosition() +export const calculateSelectionPoint = ( + stage: Stage, +): PointerPosition | undefined => { + const stagePosition = stage.position(); + const pointerPosition = stage.getPointerPosition(); - if (!pointerPosition) return + if (!pointerPosition) { + return; + } - const x = roundTo((pointerPosition.x - stagePosition.x) / stage.scaleX(), 2) - const y = roundTo((pointerPosition.y - stagePosition.y) / stage.scaleX(), 2) + const x = roundTo((pointerPosition.x - stagePosition.x) / stage.scaleX(), 2); + const y = roundTo((pointerPosition.y - stagePosition.y) / stage.scaleX(), 2); - return { x, y } -} + return { x, y }; +}; export const onSelectionStart = ( event?: KonvaEventObject, layer?: Layer, rect?: Rect, selectionEnabled?: boolean, -) => { - if (!selectionEnabled || !layer || !rect || !event) return +): void => { + if (selectionEnabled !== true || !layer || !rect || !event) { + return; + } - const stage = layer.getStage() - const firstPoint = calculateSelectionPoint(stage) + const stage = layer.getStage(); + const firstPoint = calculateSelectionPoint(stage); - if (!firstPoint) return + if (!firstPoint) { + return; + } - rect.setAttrs({ x1: firstPoint.x, y1: firstPoint.y }) - rect.visible(true) - rect.width(0) - rect.height(0) - layer.draw() -} + selectionAnchor.set(rect, firstPoint); + rect.setAttrs({ x1: firstPoint.x, y1: firstPoint.y }); + rect.visible(true); + rect.width(0); + rect.height(0); + layer.draw(); +}; -export const onSelectionMove = (layer?: Layer, rect?: Rect) => { - const stage = layer?.getStage() +export const onSelectionMove = (layer?: Layer, rect?: Rect): void => { + const stage = layer?.getStage(); - if (!stage || !rect || !layer) return + if (!stage || !rect || !layer) { + return; + } // no nothing if we didn't start selection - if (!rect.visible()) return + if (!rect.visible()) { + return; + } + const anchor = selectionAnchor.get(rect); - const { x1, y1 } = rect.getAttrs() - const secondPoint = calculateSelectionPoint(stage) + if (!anchor) { + return; + } - if (!secondPoint) return + const secondPoint = calculateSelectionPoint(stage); + if (!secondPoint) { + return; + } rect.setAttrs({ - x: Math.min(x1, secondPoint.x), - y: Math.min(y1, secondPoint.y), - width: Math.abs(secondPoint.x - x1), - height: Math.abs(secondPoint.y - y1), - }) + x: Math.min(anchor.x, secondPoint.x), + y: Math.min(anchor.y, secondPoint.y), + width: Math.abs(secondPoint.x - anchor.x), + height: Math.abs(secondPoint.y - anchor.y), + }); - layer.batchDraw() -} + layer.batchDraw(); +}; export const onSelectionEnd = ( layer?: Layer, rect?: Rect, onShapeMultiSelect?: (shapes: AnnotationShape[]) => void, -) => { - const stage = layer?.getStage() +): void => { + const stage = layer?.getStage(); - if (!stage || !rect || !layer) return + if (!stage || !rect || !layer) { + return; + } - // no nothing if we didn't start selection - if (!rect.visible()) return + if (!rect.visible()) { + return; + } // update visibility in timeout, so we can check it in click event setTimeout(() => { - rect.visible(false) - layer.batchDraw() - }) + rect.visible(false); + layer.batchDraw(); + }); - const shapes = (stage.find(`.${KONVA_REFS.shape}`) || []) as Line[] - const box = rect.getClientRect() + const shapes = stage.find(`.${KonvaRefs.Shape}`); + const box = rect.getClientRect(); const selected = shapes .filter((shape) => Konva.Util.haveIntersection(box, shape.getClientRect())) - .map((shape) => shape.getAttr('shape')) + .map((node) => getShapeFromNode(node)) + .filter((shape) => shape !== undefined); - if (selected.length) onShapeMultiSelect?.(selected) + if (selected.length > 0) { + onShapeMultiSelect?.(selected); + } - layer.batchDraw() -} + layer.batchDraw(); +}; diff --git a/src/utils/useEventListener.ts b/src/utils/useEventListener.ts index 8db8d4f..d30b519 100644 --- a/src/utils/useEventListener.ts +++ b/src/utils/useEventListener.ts @@ -1,51 +1,23 @@ -import { RefObject, useEffect, useRef } from 'react' - -export default function useEventListener< - T extends HTMLElement = HTMLDivElement, - E = Event, ->( - eventName: string, - - handler: (event: E) => void, - - element?: RefObject, -) { - // Create a ref that stores handler - - const savedHandler = useRef<(event: E) => void>(null) - +import { useEffect, useRef, type RefObject } from 'react'; + +export default function useEventListener( + eventName: K, + handler: (event: WindowEventMap[K]) => void, + element?: RefObject, +): void { + const savedHandler = useRef(handler); useEffect(() => { - // Define the listening target - - const targetElement: T | Window = element?.current || window - - if (!(targetElement && targetElement.addEventListener)) { - return - } - - // Update saved handler if necessary - - if (savedHandler.current !== handler) { - savedHandler.current = handler - } - - // Create event listener that calls handler function stored in ref - - const eventListener = (event: E) => { - // eslint-disable-next-line no-extra-boolean-cast - - if (savedHandler?.current) { - savedHandler.current(event) - } - } - // @ts-expect-error - targetElement.addEventListener(eventName, eventListener) - - // Remove event listener on cleanup + savedHandler.current = handler; + }); + useEffect(() => { + const target: HTMLElement | Window = element?.current ?? window; + const listener = (event: Event): void => { + savedHandler.current(event as WindowEventMap[K]); + }; + target.addEventListener(eventName, listener); return () => { - // @ts-expect-error - targetElement.removeEventListener(eventName, eventListener) - } - }, [eventName, element, handler]) + target.removeEventListener(eventName, listener); + }; + }, [eventName, element]); } diff --git a/src/utils/useMultiSelection.ts b/src/utils/useMultiSelection.ts index b58d21b..dc6052c 100644 --- a/src/utils/useMultiSelection.ts +++ b/src/utils/useMultiSelection.ts @@ -1,44 +1,45 @@ -import { MutableRefObject } from 'react' -import Konva from 'konva' +import { useRef, type RefObject } from 'react'; +import type Konva from 'konva'; -import useEventListener from './useEventListener' +import useEventListener from './useEventListener'; + +type Props = { + stageRef: RefObject; + isSelectionActiveRef: RefObject; + isEnabled: boolean; +}; + +const isSelectionModifier = (event: KeyboardEvent): boolean => + event.ctrlKey || + event.altKey || + event.key === 'Control' || + event.key === 'Alt'; -interface Props { - stage: Konva.Stage | null - isSelectionActiveRef: MutableRefObject -} export default function useMultiSelection({ - stage, + stageRef, isSelectionActiveRef, -}: Props) { - useEventListener( - 'keydown', - (event: KeyboardEvent) => { - event.stopPropagation() - if ( - event.ctrlKey || - event.altKey || - event.key === 'Control' || - event.key === 'Alt' - ) { - stage?.draggable(false) - isSelectionActiveRef.current = true - } - }, - ) - useEventListener( - 'keyup', - (event: KeyboardEvent) => { - event.stopPropagation() - if ( - event.ctrlKey || - event.altKey || - event.key === 'Control' || - event.key === 'Alt' - ) { - stage?.draggable(true) - isSelectionActiveRef.current = false - } - }, - ) + isEnabled, +}: Props): void { + const wasDraggableRef = useRef(false); + + useEventListener('keydown', (event) => { + event.stopPropagation(); + if (!isEnabled || !isSelectionModifier(event)) { + return; + } + // Key repeat fires keydown repeatedly: only capture the pre-selection state once. + if (!isSelectionActiveRef.current) { + wasDraggableRef.current = stageRef.current?.draggable() ?? false; + } + stageRef.current?.draggable(false); + isSelectionActiveRef.current = true; + }); + useEventListener('keyup', (event) => { + event.stopPropagation(); + if (!isSelectionActiveRef.current || !isSelectionModifier(event)) { + return; + } + stageRef.current?.draggable(wasDraggableRef.current); + isSelectionActiveRef.current = false; + }); } diff --git a/src/utils/zoom.spec.ts b/src/utils/zoom.spec.ts index bb8ecbd..dc308ce 100644 --- a/src/utils/zoom.spec.ts +++ b/src/utils/zoom.spec.ts @@ -1,39 +1,39 @@ -import Konva from 'konva' +import type Konva from 'konva'; + +import type { AnnotationViewerOptions } from '@/common/types'; import { calculateLensZoom, calculateStageZoom, calculateZoomScale, -} from '@/utils/zoom' - -import { AnnotationViewerOptions } from '..' +} from '@/utils/zoom'; describe('zoom', () => { describe('calculateLensZoom', () => { it('should return the correct position', () => { - const pointerPosition = { x: 0.5, y: 0.5 } + const pointerPosition = { x: 0.5, y: 0.5 }; const imageBoundingBox = { x: 0, y: 75, width: 600, height: 450, scale: 0.586, - } - const stage = { width: () => 600, height: () => 600 } - const zoomLevel = 1.5 + }; + const stage = { width: () => 600, height: () => 600 }; + const zoomLevel = 1.5; - const expected = { x: -467.92, y: -275.94 } + const expected = { x: -467.92, y: -275.94 }; const actual = calculateLensZoom( pointerPosition, imageBoundingBox, stage as Konva.Stage, zoomLevel, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) + expect(actual).to.deep.equal(expected); + }); + }); describe('calculateStageZoom', () => { it('should return the correct scale and position', () => { @@ -41,24 +41,23 @@ describe('zoom', () => { scaleX: () => 1.12464, position: () => ({ x: -82, y: -176 }), getPointerPosition: () => ({ x: 350, y: 400 }), - } - const deltaY = -60 - const options = { zoom: { modifier: 1.2 } } + }; + const deltaY = -60; + const options = { zoom: { modifier: 1.2 } }; - const expected = { - newScale: 1.35, - newPos: { x: -168.4, y: -291.2 }, - } + const expectedPos = { x: -168.4, y: -291.2 }; const actual = calculateStageZoom( stage as Konva.Stage, deltaY, options as AnnotationViewerOptions, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) + // Scale is kept at full precision; only positions are rounded. + expect(actual?.newScale).to.be.closeTo(1.12464 * 1.2, 1e-9); + expect(actual?.newPos).to.deep.equal(expectedPos); + }); + }); describe('calculateZoomScale', () => { it('should return the correct scale and position', () => { @@ -66,28 +65,26 @@ describe('zoom', () => { scaleX: () => 1.12464, position: () => ({ x: -82, y: -176 }), getPointerPosition: () => ({ x: 350, y: 400 }), - } + }; const imageBoundingBox = { x: 0, y: 75, width: 600, height: 450, scale: 0.586, - } - const deltaY = -60 + }; + const deltaY = -60; - const expected = { - newScale: -35.16, - newPos: { x: 12242.59, y: 15181.35 }, - } + const expectedPos = { x: 12242.59, y: 15181.35 }; const actual = calculateZoomScale( stage as Konva.Stage, deltaY, imageBoundingBox, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) -}) + expect(actual.newScale).to.be.closeTo(-60 * 0.586, 1e-9); + expect(actual.newPos).to.deep.equal(expectedPos); + }); + }); +}); diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts index 64e6a6d..9da970a 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -1,132 +1,153 @@ -import Konva from 'konva' -import { KonvaEventObject } from 'konva/lib/Node' +import type Konva from 'konva'; +import type { KonvaEventObject } from 'konva/lib/Node'; -import { DEFAULT_LENS_ZOOM_LEVEL } from '@/common/constants' -import { +import { DEFAULT_LENS_ZOOM_LEVEL } from '@/common/constants'; +import type { AnnotationViewerOptions, ImageBoundingBox, PointerPosition, -} from '@/common/types' +} from '@/common/types'; -import { roundTo } from '@/utils/roundTo' +import { roundTo } from '@/utils/roundTo'; + +const ZOOM_SCALE_ATTR = 'zoomScale'; + +/** Zoom factor relative to the fitted image (1 = fit to container). */ +export const getZoomScale = (stage: Konva.Stage): number => { + const value: unknown = stage.getAttr(ZOOM_SCALE_ATTR); + return typeof value === 'number' ? value : 1; +}; + +export const setZoomScale = (stage: Konva.Stage, zoomScale: number): void => { + stage.setAttr(ZOOM_SCALE_ATTR, zoomScale); +}; export const calculateZoomScale = ( stage: Konva.Stage, zoomScale: number, imageBoundingBox: ImageBoundingBox, -) => { - const oldScale = stage.scaleX() +): { newScale: number; newPos: PointerPosition } => { + const oldScale = stage.scaleX(); - const stagePosition = stage.position() + const stagePosition = stage.position(); const pointerPosition = { x: 0.5 * imageBoundingBox.width + imageBoundingBox.x, y: 0.5 * imageBoundingBox.height + imageBoundingBox.y, - } + }; const mousePointTo = { x: (pointerPosition.x - stagePosition.x) / oldScale, y: (pointerPosition.y - stagePosition.y) / oldScale, - } + }; - const newScale = zoomScale * imageBoundingBox.scale + const newScale = zoomScale * imageBoundingBox.scale; const newPos = { x: roundTo(pointerPosition.x - mousePointTo.x * newScale, 2), y: roundTo(pointerPosition.y - mousePointTo.y * newScale, 2), - } + }; - return { newScale: roundTo(newScale, 2), newPos } -} + return { newScale, newPos }; +}; export const handleZoomScale = ( stage: Konva.Stage | null, zoomScale: number, imageBoundingBox: ImageBoundingBox | null, -) => { - if (!stage || !imageBoundingBox) return +): void => { + if (!stage || !imageBoundingBox) { + return; + } const { newScale, newPos } = calculateZoomScale( stage, zoomScale, imageBoundingBox, - ) + ); if (newScale < imageBoundingBox.scale) { - stage.draggable(false) - stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }) - stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }) - stage.setAttr('zoomScale', 1) + stage.draggable(false); + stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }); + stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }); + setZoomScale(stage, 1); } else { - stage.draggable(true) - stage.scale({ x: newScale, y: newScale }) - stage.setAttr('zoomScale', newScale / imageBoundingBox.scale) - stage.position(newPos) + stage.draggable(true); + stage.scale({ x: newScale, y: newScale }); + setZoomScale(stage, newScale / imageBoundingBox.scale); + stage.position(newPos); } - stage.batchDraw() -} + stage.batchDraw(); +}; export const calculateStageZoom = ( stage: Konva.Stage, deltaY: number, options: AnnotationViewerOptions, -) => { - const oldScale = stage.scaleX() +): { newScale: number; newPos: PointerPosition } | undefined => { + const oldScale = stage.scaleX(); - const stagePosition = stage.position() - const pointerPosition = stage.getPointerPosition() || { x: 0, y: 0 } + const stagePosition = stage.position(); + const pointerPosition = stage.getPointerPosition() ?? { x: 0, y: 0 }; const mousePointTo = { x: (pointerPosition.x - stagePosition.x) / oldScale, y: (pointerPosition.y - stagePosition.y) / oldScale, - } + }; - const { modifier } = options.zoom! + if (!options.zoom) { + return; + } + const { modifier } = options.zoom; - const newScale = deltaY < 0 ? oldScale * modifier : oldScale / modifier + const newScale = deltaY < 0 ? oldScale * modifier : oldScale / modifier; const newPos = { x: roundTo(pointerPosition.x - mousePointTo.x * newScale, 2), y: roundTo(pointerPosition.y - mousePointTo.y * newScale, 2), - } + }; - return { newScale: roundTo(newScale, 2), newPos } -} + return { newScale, newPos }; +}; export const handleStageZoom = ( stage: Konva.Stage | null, imageBoundingBox: ImageBoundingBox | null, event: KonvaEventObject, options: AnnotationViewerOptions, -) => { - if (!stage || !imageBoundingBox) return +): void => { + if (!stage || !imageBoundingBox || !options.zoom) { + return; + } - event.evt.preventDefault() + event.evt.preventDefault(); - const { max } = options.zoom! + const { max } = options.zoom; - const { newScale, newPos } = calculateStageZoom( - stage, - event.evt.deltaY, - options, - ) + const stageZoom = calculateStageZoom(stage, event.evt.deltaY, options); + if (!stageZoom) { + return; + } + const { newScale, newPos } = stageZoom; - if (newScale > max) return + if (newScale / imageBoundingBox.scale > max) { + return; + } if (newScale < imageBoundingBox.scale) { - stage.draggable(false) - stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }) - stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }) - stage.setAttr('zoomScale', 1) + stage.draggable(false); + stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }); + stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }); + setZoomScale(stage, 1); } else { - stage.draggable(true) - stage.scale({ x: newScale, y: newScale }) - stage.setAttr('zoomScale', newScale / imageBoundingBox.scale) - stage.position(newPos) + stage.draggable(true); + stage.scale({ x: newScale, y: newScale }); + setZoomScale(stage, newScale / imageBoundingBox.scale); + stage.position(newPos); } - stage.batchDraw() -} + stage.batchDraw(); +}; export const calculateLensZoom = ( pointerPosition: PointerPosition, @@ -135,32 +156,34 @@ export const calculateLensZoom = ( zoomLevel: number, ): PointerPosition => { const pointerX = - (pointerPosition.x * imageBoundingBox.width) / imageBoundingBox.scale + (pointerPosition.x * imageBoundingBox.width) / imageBoundingBox.scale; const pointerY = - (pointerPosition.y * imageBoundingBox.height) / imageBoundingBox.scale + (pointerPosition.y * imageBoundingBox.height) / imageBoundingBox.scale; return { x: roundTo(-pointerX * zoomLevel + stage.width() / 2, 2), y: roundTo(-pointerY * zoomLevel + stage.height() / 2, 2), - } -} + }; +}; export const handleLensZoom = ( stage: Konva.Stage | null, imageBoundingBox: ImageBoundingBox | null, pointerPosition: PointerPosition, zoomLevel = DEFAULT_LENS_ZOOM_LEVEL, -) => { - if (!stage || !imageBoundingBox) return +): void => { + if (!stage || !imageBoundingBox) { + return; + } const newPos = calculateLensZoom( pointerPosition, imageBoundingBox, stage, zoomLevel, - ) + ); - stage.scale({ x: zoomLevel, y: zoomLevel }) - stage.position(newPos) - stage.batchDraw() -} + stage.scale({ x: zoomLevel, y: zoomLevel }); + stage.position(newPos); + stage.batchDraw(); +}; diff --git a/tsconfig.json b/tsconfig.json index b2dfa29..318602a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,27 @@ { "compilerOptions": { - "jsx": "react", - "target": "ES2020", - "module": "ES6", - "declaration": true, - "outDir": "dist", - "moduleResolution": "node", + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "types": ["node", "cypress", "vite/client"], "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "verbatimModuleSyntax": true, "esModuleInterop": true, - "baseUrl": ".", + "declaration": true, + "outDir": "dist", "paths": { - "@/*": ["src/*"], + "@/*": ["./src/*"], "cypress/*": ["./cypress/*"] }, "skipLibCheck": true }, - "include": ["src", "cypress"], + "include": ["src", "cypress", "*.config.ts"], "exclude": ["node_modules", "dist", "docs"] } diff --git a/vite.config.js b/vite.config.js deleted file mode 100644 index 98fce8c..0000000 --- a/vite.config.js +++ /dev/null @@ -1,25 +0,0 @@ -import { resolve } from 'path' -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' -import dts from 'vite-plugin-dts' - -export default defineConfig({ - assetsInclude: ['**/*.tiff', '**/*.heic'], - resolve: { - alias: { - '@/': new URL('./src/', import.meta.url).pathname, - }, - }, - plugins: [react(), dts({ insertTypesEntry: true })], - build: { - lib: { - entry: resolve(__dirname, 'src/index.ts'), - name: 'React mindee js', - formats: ['es'], - fileName: 'index', - }, - rollupOptions: { - external: ['react', 'react-dom'], - }, - }, -}) diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..2bc2584 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,33 @@ +import { fileURLToPath } from 'node:url'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; + +export default defineConfig({ + assetsInclude: ['**/*.tiff', '**/*.heic'], + resolve: { + tsconfigPaths: true, + }, + plugins: [ + react(), + dts({ + tsconfigPath: './tsconfig.json', + include: ['src'], + exclude: ['src/**/*.spec.*'], + bundleTypes: true, + }), + ], + worker: { + format: 'es', + }, + build: { + lib: { + entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)), + formats: ['es'], + fileName: 'index', + }, + rollupOptions: { + external: ['react', 'react-dom', 'react/jsx-runtime'], + }, + }, +});