From fe567458ab37b0172c14b4dffa07f691f1d3a8cf Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:21:53 +0200 Subject: [PATCH 01/10] chore: :arrow_up: update build tools and refactor code to match new standards --- .eslintignore | 5 - .eslintrc | 46 - .prettierignore | 6 + .prettierrc | 6 +- CODE_OF_CONDUCT.md | 23 +- CONTRIBUTING.md | 50 +- ISSUE_TEMPLATE.md | 4 +- README.md | 3 +- cypress.config.ts | 4 +- cypress/.DS_Store | Bin 6148 -> 0 bytes cypress/assets/.DS_Store | Bin 6148 -> 0 bytes cypress/assets/shapes.ts | 2 +- cypress/support/component-index.html | 10 +- cypress/support/component.ts | 4 +- cypress/support/index.d.ts | 2 +- eslint.config.ts | 55 + package.json | 59 +- pnpm-lock.yaml | 6123 +++++----------------- pnpm-workspace.yaml | 1 + src/common/constants.ts | 6 +- src/common/types.ts | 27 +- src/components/AnnotationLens.spec.tsx | 4 +- src/components/AnnotationLens.tsx | 160 +- src/components/AnnotationViewer.spec.tsx | 90 +- src/components/AnnotationViewer.tsx | 368 +- src/components/dynamicZoom.spec.tsx | 46 +- src/index.ts | 6 +- src/utils/canvas.spec.ts | 6 +- src/utils/canvas.ts | 30 +- src/utils/functions.ts | 18 +- src/utils/getImagesFromPDF.spec.tsx | 8 +- src/utils/getImagesFromPDF.ts | 71 +- src/utils/getPDFPageCount.spec.tsx | 4 +- src/utils/getPDFPageCount.ts | 16 +- src/utils/image.spec.ts | 4 +- src/utils/image.ts | 31 +- src/utils/layer.ts | 2 +- src/utils/orientation.ts | 50 +- src/utils/selection.spec.ts | 2 +- src/utils/selection.ts | 38 +- src/utils/useEventListener.ts | 58 +- src/utils/useMultiSelection.ts | 61 +- src/utils/zoom.spec.ts | 6 +- src/utils/zoom.ts | 43 +- tsconfig.json | 20 +- vite.config.js | 25 - vite.config.ts | 32 + 47 files changed, 2183 insertions(+), 5452 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc create mode 100644 .prettierignore delete mode 100644 cypress/.DS_Store delete mode 100644 cypress/assets/.DS_Store create mode 100644 eslint.config.ts delete mode 100644 vite.config.js create mode 100644 vite.config.ts 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/.prettierignore b/.prettierignore new file mode 100644 index 0000000..97809e4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist +docs +node_modules +pnpm-lock.yaml +cypress/snapshots +CHANGELOG.md diff --git a/.prettierrc b/.prettierrc index bc7735d..f058170 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,6 +5,7 @@ "trailingComma": "all", "semi": false, "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..eef7680 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 @@ -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..0dca3a7 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,8 @@ 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 { AnnotationViewer } from 'react-mindee-js' const dummyShapes = [ { diff --git a/cypress.config.ts b/cypress.config.ts index bd43a4a..5a2755d 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,5 +1,5 @@ +import { addMatchImageSnapshotPlugin } from '@simonsmith/cypress-image-snapshot/plugin' import { defineConfig } from 'cypress' -import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin' import { defineConfig as defineViteConfig } from 'vite' import tsconfigPaths from 'vite-tsconfig-paths' @@ -17,7 +17,7 @@ export default defineConfig({ // if (config.testingType === 'component') // on('dev-server:start', async (options) => startDevServer({ options })) - addMatchImageSnapshotPlugin(on, config) + addMatchImageSnapshotPlugin(on) return config }, diff --git a/cypress/.DS_Store b/cypress/.DS_Store deleted file mode 100644 index 1aa27bd5c00f2209cec77435ea582b72b0761501..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z<-5O({YS3Oz1(E!e75#7l_v1&ruHr6we3FlI}WnnNk%tS{t~_&m<+ zZVthKHxWBicE8#A+0A^={b7u8cNvZuvl(L+G(?U{gP^(5wPk`4xtb$qSx{v2Adx}C zM1RqQ-`-(9%itHW`uBeX^CZsl(dd(Jwc6XAuGO{r*1h*63orNcW#;;eTO3_UnFN*Y z2iI{_% zho~P-*6W_Nw|{VUIeSiCQu(Hd>;Qk<>Avy+2jcV(F4zJJXZzH0Bj&BJ>VbC#HY6K4m*QtOy zm76C9*XdvvCeATfYSihBtCe9MvvT!#;c9iT3l+||qmg=IfEcJV(9%O2&;JYf%QQan z>nSuM28e-w#sF_my{QXDnX~mUYdpu)?H{4)Kbo&bm=-Kr! z!Uh=%RL^hCnLb{y#o}R=O~$KiY-@o#V*lIBQoO{Nr=H=Jr@rCoZ+ZT6at#~Y&jz<^ zzm*V6qc1V3Gg^xVqf=oVc{{motgl8Wxc?mUdIZboqn6JaJFKY3(y|$0!8bdakGvm5 z%ctSktN?4aTIV95x2}LI;0o*&(C$g#fw}@`Vc669fB*aazfSUzE8q(JD+Q!Id>js#Qmm~9$!V=k=udPtT~`L& kQ`oUm%(YsIujpo+Pqaf!BbEU + - - - + + + Components App
- \ No newline at end of file + diff --git a/cypress/support/component.ts b/cypress/support/component.ts index e7c2d99..437ee8f 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -1,5 +1,5 @@ -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, diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts index 507e83f..43a27b3 100644 --- a/cypress/support/index.d.ts +++ b/cypress/support/index.d.ts @@ -1,4 +1,4 @@ -import { mount } from 'cypress/react18' +import type { mount } from 'cypress/react' declare global { namespace Cypress { diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..8d2fc8f --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,55 @@ +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'], + { + languageOptions: { + globals: { ...globals.browser, ...globals.es2022 }, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + '@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/consistent-type-imports': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + 'no-console': ['error', { allow: ['warn', 'error'] }], + }, + }, + { + files: ['**/*.spec.{ts,tsx}', 'cypress/**/*.ts'], + languageOptions: { + globals: { ...globals.mocha, cy: 'readonly', Cypress: 'readonly' }, + }, + rules: { + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + }, + }, + { + files: ['*.config.ts'], + languageOptions: { globals: globals.node }, + }, + prettier, +) diff --git a/package.json b/package.json index a737a14..5b221f9 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" }, @@ -26,7 +27,7 @@ "CHANGELOG.md" ], "engines": { - "node": ">=22.13" + "node": ">=22" }, "packageManager": "pnpm@12.8.1", "dependencies": { @@ -37,34 +38,30 @@ "uuid": "^8.3.2" }, "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", + "@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", + "@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", + "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", + "vite-tsconfig-paths": "^6.1.1" }, "peerDependencies": { "react": "^18 || ^19", @@ -74,7 +71,6 @@ "preinstall": "npx only-allow pnpm", "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", @@ -84,9 +80,10 @@ "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 . && prettier --check .", + "lint:fix": "eslint --fix . && prettier --write ." }, "keywords": [ "react", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a43e9b6..09ab000 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,24 +182,21 @@ importers: 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) + '@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 @@ -212,60 +209,51 @@ importers: '@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.1(@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 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.1 + version: 8.3.1(@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)) + 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.1(@types/node@24.19.0)(jiti@2.7.0)) 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: ^6.1.1 + version: 6.1.1(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0)) packages: @@ -303,10 +291,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 +312,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 +324,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 +347,54 @@ 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} + + '@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,58 +414,6 @@ 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'} - - '@jest/console@26.6.2': - resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==} - engines: {node: '>= 10.14.2'} - - '@jest/core@26.6.3': - resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==} - engines: {node: '>= 10.14.2'} - - '@jest/environment@26.6.2': - resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==} - engines: {node: '>= 10.14.2'} - - '@jest/fake-timers@26.6.2': - resolution: {integrity: sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==} - engines: {node: '>= 10.14.2'} - - '@jest/globals@26.6.2': - resolution: {integrity: sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==} - engines: {node: '>= 10.14.2'} - - '@jest/reporters@26.6.2': - resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==} - engines: {node: '>= 10.14.2'} - - '@jest/source-map@26.6.2': - resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==} - engines: {node: '>= 10.14.2'} - - '@jest/test-result@26.6.2': - resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==} - engines: {node: '>= 10.14.2'} - - '@jest/test-sequencer@26.6.3': - resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==} - engines: {node: '>= 10.14.2'} - - '@jest/transform@26.6.2': - resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} - engines: {node: '>= 10.14.2'} - - '@jest/types@26.6.2': - resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} - engines: {node: '>= 10.14.2'} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -715,37 +430,135 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@mapbox/node-pre-gyp@1.0.11': resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true - '@microsoft/api-extractor-model@7.28.13': - resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + '@microsoft/api-extractor-model@7.33.14': + resolution: {integrity: sha512-HdFzU14NGrm04SxA8CfEByX4R1S06o08qVCrGgIepb4miUgky771TwiDi2b3/ZJSQ1QKEJzq452MW09zenL19w==} + engines: {node: '>=20.9.0'} - '@microsoft/api-extractor@7.43.0': - resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + '@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'} + '@oxc-project/types@0.151.0': + resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@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] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@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] + + '@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] + + '@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] + + '@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-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] + + '@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] + + '@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/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@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-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-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-musl@1.2.11': + resolution: {integrity: sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==} + 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-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-x64-msvc@1.2.11': + resolution: {integrity: sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==} + 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==} @@ -756,88 +569,64 @@ packages: rollup: optional: true - '@rushstack/node-core-library@4.0.2': - resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + '@rushstack/node-core-library@5.25.0': + resolution: {integrity: sha512-JWd5MVInIQqTlujjqV2gO4aJi/QZvBdGCD5jQ4tF0Swcz1Ba80fP0dvxIYTaHawUujyQaCixtzzz9LM8bM26hQ==} + engines: {node: '>=20.9.0'} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/rig-package@0.5.2': - resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} - - '@rushstack/terminal@0.10.0': - resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/ts-command-line@4.19.1': - resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + '@rushstack/rig-package@0.7.3': + resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==} - '@sinonjs/commons@1.8.6': - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} + '@rushstack/terminal@0.24.6': + resolution: {integrity: sha512-MuSbKmMnP8CVEEBZ2kJpE22farLfxrScOrJ3pN3PoCOSaxemEhD/0H1lXZYj2PmKJxQMHb5pib5rVfIlOX/DlA==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true - '@sinonjs/fake-timers@6.0.1': - resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==} + '@rushstack/ts-command-line@5.3.16': + resolution: {integrity: sha512-k1L7MQQEuQ1v0rsQ8pRzoZ3NRsrvSifBguWlLnl2a5iWGTTW9E0/G/B39291eAf3zF45wheybuJ2onwB/PPsfA==} + engines: {node: '>=20.9.0'} - '@tootallnate/once@1.1.2': - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} + '@simonsmith/cypress-image-snapshot@11.0.0': + resolution: {integrity: sha512-fqiH4/0DARt4vR8U4d7mMyrWAdKlirG+0dNksZbMbP6a4zRPo6q7EmOSvRuse8kShbmrg7UogvlApjE9mN+crQ==} + peerDependencies: + cypress: '>=15.10.0' '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - - '@types/cypress-image-snapshot@3.1.9': - resolution: {integrity: sha512-vcFgB8AfuM0dtupCHAGgapUbjRNWmMDtpzzpXz5gexWOHW4y/n10XRi5/y5Xpi0Ztku+X4dnRVMqQa5IDcy3Ig==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@27.5.2': - resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@16.18.126': - resolution: {integrity: sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==} - '@types/node@20.19.43': resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/node@24.19.0': + resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==} - '@types/prettier@2.7.3': - resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} + '@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==} @@ -847,17 +636,14 @@ packages: '@types/react@19.3.0': resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} - '@types/semver@7.8.0': - resolution: {integrity: sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==} - '@types/sinonjs__fake-timers@8.1.1': resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} '@types/sizzle@2.3.10': resolution: {integrity: sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/tmp@0.2.6': + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} '@types/utif@3.0.6': resolution: {integrity: sha512-4XHDJkLHoUeLA2P/VOd6dpDJpSTpV84tN9EyHojZrW22jtUR0VrdLwVRUAw+oI3g72vSzOo+YQOzfwcW6WYHLw==} @@ -865,141 +651,103 @@ packages: '@types/uuid@8.3.4': resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + '@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' + + '@typescript-eslint/parser@8.71.0': + resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==} + 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' - '@types/yargs@15.0.20': - resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==} + '@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' - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@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} - '@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} + '@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-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@5.62.0': - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.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: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@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/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/type-utils@5.62.0': - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.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: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@5.62.0': - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@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} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@5.62.0': - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.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} - - '@ungap/structured-clone@1.4.0': - resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} - - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} - engines: {node: ^14.18.0 || >=16.0.0} + '@typescript-eslint/utils@8.71.0': + resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==} + 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/visitor-keys@8.71.0': + resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@volar/source-map@1.11.1': - resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} - - '@volar/typescript@1.11.1': - resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - typescript: '*' + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: ^0.145.0 + vite: ^8.0.0 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'} - - 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==} - 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'} @@ -1009,64 +757,51 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - 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'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} aproba@2.1.0: resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} - arch@2.2.0: - resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + arch@3.0.0: + resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} @@ -1076,29 +811,6 @@ packages: 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==} - engines: {node: '>=0.10.0'} - asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -1106,17 +818,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 +825,22 @@ 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'} @@ -1187,40 +858,20 @@ packages: 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'} + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} cachedir@2.4.0: resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} @@ -1234,18 +885,6 @@ 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==} @@ -1253,91 +892,41 @@ packages: 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'} - 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'} - - 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==} + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} 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==} @@ -1348,101 +937,65 @@ packages: 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-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==} + + confbox@0.3.1: + resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - 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'} - 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,17 +1013,6 @@ 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'} @@ -1478,22 +1020,6 @@ packages: 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'} @@ -1505,30 +1031,9 @@ packages: 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 +1045,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 +1054,9 @@ 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'} - - 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 +1074,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 +1128,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 +1142,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,123 +1153,59 @@ 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'} - - 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==} + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} - 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'} - - 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==} @@ -1848,22 +1235,14 @@ packages: 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,36 +1251,20 @@ 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-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + 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 @@ -1910,17 +1273,9 @@ packages: 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'} + globals@17.12.0: + resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} + engines: {node: '>=18'} globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} @@ -1935,20 +1290,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'} @@ -1964,49 +1305,35 @@ packages: 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==} + http-signature@1.4.0: + resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} https-proxy-agent@5.0.1: @@ -2017,10 +1344,6 @@ packages: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2028,27 +1351,18 @@ packages: 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. @@ -2056,64 +1370,14 @@ packages: 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 - - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - 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 +1386,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 +1398,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 +1413,23 @@ 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'} - 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,40 +1438,20 @@ 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-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==} @@ -2418,33 +1466,14 @@ 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==} 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'} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - 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==} @@ -2452,59 +1481,99 @@ packages: 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'} - 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==} + 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] - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + 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'} - 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. + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} 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,17 +1581,13 @@ 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@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==} @@ -2530,21 +1595,6 @@ packages: 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'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -2552,18 +1602,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,20 +1614,25 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@2.1.0: resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} engines: {node: '>=8'} - minimatch@3.0.8: - resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2605,28 +1648,17 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - mixin-deep@1.3.2: - resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} - engines: {node: '>=0.10.0'} - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - 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==} @@ -2635,19 +1667,9 @@ packages: 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} @@ -2657,12 +1679,6 @@ packages: encoding: optional: true - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-notifier@8.0.2: - resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==} - node-releases@2.0.57: resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} engines: {node: '>=18'} @@ -2672,21 +1688,6 @@ packages: engines: {node: '>=6'} hasBin: true - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-path@2.1.1: - resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} - engines: {node: '>=0.10.0'} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -2695,29 +1696,14 @@ packages: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. - nwsapi@2.2.28: - resolution: {integrity: sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-copy@0.1.0: - resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} - engines: {node: '>=0.10.0'} - 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 +1711,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,67 +1722,20 @@ 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-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - 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'} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} 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'} @@ -2801,10 +1744,6 @@ packages: 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,14 +1751,13 @@ 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'} - 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'} @@ -2833,10 +1771,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 +1779,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,10 +1797,6 @@ 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@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} @@ -2885,41 +1805,22 @@ packages: 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} - 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'} - 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,172 +1828,79 @@ 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'} - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - - regex-not@1.0.2: - resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} - engines: {node: '>=0.10.0'} - - remove-trailing-separator@1.1.0: - resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} - - 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==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} 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 - hasBin: true - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rolldown@1.2.11: + resolution: {integrity: sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@3.30.0: resolution: {integrity: sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==} 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 @@ -3104,34 +1912,14 @@ packages: 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,82 +1939,32 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} simple-get@3.1.1: resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} - 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'} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} - snapdragon@0.8.2: - resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} - engines: {node: '>=0.10.0'} + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - 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 - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map-url@0.4.1: - resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} - deprecated: See https://github.com/lydell/source-map-url#deprecated - - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - 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-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - 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'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -3238,61 +1976,37 @@ 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'} - 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-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.3.0: + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + engines: {node: '>=20'} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} - 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,85 +2015,55 @@ 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==} + 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 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==} - - throat@5.0.0: - resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} - 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'} + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} 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==} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>2.7.0' + typescript: '>=4.8.4' tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} @@ -3392,18 +2076,6 @@ packages: typescript: optional: true - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -3414,66 +2086,73 @@ 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 + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + 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'} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} 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: '>=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,17 +2167,6 @@ 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==} @@ -3510,118 +2178,83 @@ packages: 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'} - 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: '>=3' peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + rollup: + optional: true vite: optional: true - vite-tsconfig-paths@4.3.2: - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + vite-tsconfig-paths@6.1.1: + resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} 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.1: + resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} + 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 + 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 + jiti: optional: true - lightningcss: + 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==} + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} 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==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} 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'} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3634,20 +2267,13 @@ packages: 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==} - ws@7.5.13: resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} engines: {node: '>=8.3.0'} @@ -3660,44 +2286,28 @@ 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'} - - 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: ^3.25.0 || ^4.0.0 + + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} snapshots: @@ -3763,8 +2373,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,92 +2388,7 @@ 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))': - dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-bigint@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 - - '@babel/plugin-syntax-class-properties@7.12.13(@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 - - '@babel/plugin-syntax-class-static-block@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 - - '@babel/plugin-syntax-import-attributes@7.29.7(@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 - - '@babel/plugin-syntax-import-meta@7.10.4(@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 - - '@babel/plugin-syntax-json-strings@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 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@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 - - '@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 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@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 - - '@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 - - '@babel/plugin-syntax-optional-catch-binding@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 - - '@babel/plugin-syntax-optional-chaining@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 - - '@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 - - '@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 - - '@babel/plugin-transform-react-jsx-self@7.29.7(@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 - - '@babel/plugin-transform-react-jsx-source@7.29.7(@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 - - '@babel/template@7.29.7': + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/parser': 7.29.9 @@ -3888,25 +2411,19 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@bcoe/v8-coverage@0.2.3': {} - - '@cnakazawa/watch@1.0.4': + '@cacheable/memory@2.2.0': dependencies: - exec-sh: 0.3.6 - minimist: 1.2.8 - - '@colors/colors@1.5.0': - optional: true + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 - '@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)': + '@cacheable/utils@2.5.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 + hashery: 1.5.1 + keyv: 5.6.0 - '@cypress/request@2.88.12': + '@cypress/request@4.0.1': dependencies: aws-sign2: 0.7.0 aws4: 1.13.2 @@ -3914,26 +2431,17 @@ snapshots: combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 - form-data: 2.3.3 - http-signature: 1.3.6 + 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.10.7 + qs: 6.16.0 safe-buffer: 5.2.1 - tough-cookie: 4.1.4 + tough-cookie: 5.1.2 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: @@ -3942,278 +2450,67 @@ snapshots: transitivePeerDependencies: - supports-color - '@esbuild/android-arm64@0.18.20': - optional: true - - '@esbuild/android-arm@0.18.20': - optional: true - - '@esbuild/android-x64@0.18.20': - optional: true - - '@esbuild/darwin-arm64@0.18.20': - optional: true - - '@esbuild/darwin-x64@0.18.20': - optional: true - - '@esbuild/freebsd-arm64@0.18.20': - optional: true - - '@esbuild/freebsd-x64@0.18.20': - optional: true - - '@esbuild/linux-arm64@0.18.20': - optional: true - - '@esbuild/linux-arm@0.18.20': - optional: true - - '@esbuild/linux-ia32@0.18.20': - optional: true - - '@esbuild/linux-loong64@0.18.20': - optional: true - - '@esbuild/linux-mips64el@0.18.20': - optional: true - - '@esbuild/linux-ppc64@0.18.20': - optional: true - - '@esbuild/linux-riscv64@0.18.20': - optional: true - - '@esbuild/linux-s390x@0.18.20': - optional: true - - '@esbuild/linux-x64@0.18.20': - optional: true - - '@esbuild/netbsd-x64@0.18.20': - optional: true - - '@esbuild/openbsd-x64@0.18.20': - optional: true - - '@esbuild/sunos-x64@0.18.20': - optional: true - - '@esbuild/win32-arm64@0.18.20': - optional: true - - '@esbuild/win32-ia32@0.18.20': - optional: true - - '@esbuild/win32-x64@0.18.20': - optional: true - - '@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))': dependencies: - eslint: 8.57.1(supports-color@8.1.1) + eslint: 10.11.0(jiti@2.7.0)(supports-color@8.1.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/eslintrc@2.1.4(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 - transitivePeerDependencies: - - supports-color - - '@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)': dependencies: - '@humanwhocodes/object-schema': 2.0.3 + '@eslint/object-schema': 3.0.5 debug: 4.4.3(supports-color@8.1.1) - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} - - '@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@2.8.8)(supports-color@8.1.1)': - 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 - semver: 7.8.5 + minimatch: 10.2.6 transitivePeerDependencies: - supports-color - '@istanbuljs/load-nyc-config@1.1.0': + '@eslint/config-helpers@0.7.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 + '@eslint/core': 1.2.1 - '@istanbuljs/schema@0.1.6': {} - - '@jest/console@26.6.2': + '@eslint/core@1.2.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 + '@types/json-schema': 7.0.15 - '@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 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate + '@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) - '@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 + '@eslint/object-schema@3.0.5': {} - '@jest/fake-timers@26.6.2': + '@eslint/plugin-kit@0.7.3': 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 + '@eslint/core': 1.2.1 + levn: 0.4.1 - '@jest/globals@26.6.2': + '@humanfs/core@0.19.2': dependencies: - '@jest/environment': 26.6.2 - '@jest/types': 26.6.2 - expect: 26.6.2 + '@humanfs/types': 0.15.0 - '@jest/reporters@26.6.2(supports-color@8.1.1)': + '@humanfs/node@0.16.8': 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 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 - '@jest/source-map@26.6.2': - dependencies: - callsites: 3.1.0 - graceful-fs: 4.2.11 - source-map: 0.6.1 + '@humanfs/types@0.15.0': {} - '@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 + '@humanwhocodes/module-importer@1.0.1': {} - '@jest/test-sequencer@26.6.3(supports-color@8.1.1)': - 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 + '@humanwhocodes/retry@0.4.3': {} - '@jest/transform@26.6.2(supports-color@8.1.1)': + '@ianvs/prettier-plugin-sort-imports@4.7.1(prettier@3.9.9)(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 + '@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: 3.9.9 + semver: 7.8.5 transitivePeerDependencies: - supports-color - '@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 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -4233,6 +2530,14 @@ snapshots: '@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': {} + '@mapbox/node-pre-gyp@1.0.11(supports-color@8.1.1)': dependencies: detect-libc: 2.1.2 @@ -4249,54 +2554,89 @@ snapshots: - supports-color optional: true - '@microsoft/api-extractor-model@7.28.13(@types/node@20.19.43)': + '@microsoft/api-extractor-model@7.33.14(@types/node@24.19.0)': dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.19.43) + '@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.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 + '@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.5.4 + semver: 7.7.4 source-map: 0.6.1 - typescript: 5.4.2 + typescript: 5.9.3 transitivePeerDependencies: - '@types/node' - '@microsoft/tsdoc-config@0.16.2': + '@microsoft/tsdoc-config@0.18.2': dependencies: - '@microsoft/tsdoc': 0.14.2 - ajv: 6.12.6 + '@microsoft/tsdoc': 0.17.0 + ajv: 8.18.0 jju: 1.4.0 - resolve: 1.19.0 + resolve: 1.22.12 - '@microsoft/tsdoc@0.14.2': {} + '@microsoft/tsdoc@0.17.0': {} - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@oxc-project/types@0.151.0': {} - '@nodelib/fs.stat@2.0.5': {} + '@rolldown/binding-android-arm-eabi@1.2.11': + optional: true - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 + '@rolldown/binding-android-arm64@1.2.11': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.11': + optional: true + + '@rolldown/binding-darwin-x64@1.2.11': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.11': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.11': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.11': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.11': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.11': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.11': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.11': + 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 +2646,74 @@ 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': 20.19.43 + '@types/node': 24.19.0 + + '@rushstack/problem-matcher@0.2.1(@types/node@24.19.0)': + optionalDependencies: + '@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': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@6.0.1': + '@simonsmith/cypress-image-snapshot@11.0.0(cypress@16.1.0)': dependencies: - '@sinonjs/commons': 1.8.6 - - '@tootallnate/once@1.1.2': {} + '@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/argparse@1.0.38': {} - '@types/babel__core@7.20.5': - 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 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.8 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.9 - '@babel/types': 7.29.8 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.8 - - '@types/cypress-image-snapshot@3.1.9': {} + '@types/esrecurse@4.3.1': {} '@types/estree@1.0.9': {} - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 20.19.43 - - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@27.5.2': - dependencies: - jest-matcher-utils: 27.5.1 - pretty-format: 27.5.1 - '@types/json-schema@7.0.15': {} - '@types/node@16.18.126': {} - '@types/node@20.19.43': dependencies: undici-types: 6.21.0 - '@types/normalize-package-data@2.4.4': {} + '@types/node@24.19.0': + dependencies: + undici-types: 7.24.6 - '@types/prettier@2.7.3': {} + '@types/pixelmatch@5.2.6': + dependencies: + '@types/node': 20.19.43 '@types/react-dom@19.3.0(@types/react@19.3.0)': dependencies: @@ -4414,193 +2723,135 @@ 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/node': 24.19.0 '@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 - - '@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': + '@typescript-eslint/project-service@8.71.0(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/type-utils@5.62.0(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1)(typescript@4.9.5)': - 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': - dependencies: - '@volar/source-map': 1.11.1 - - '@volar/source-map@1.11.1': + '@typescript-eslint/visitor-keys@8.71.0': dependencies: - muggle-string: 0.3.1 + '@typescript-eslint/types': 8.71.0 + eslint-visitor-keys: 5.0.1 - '@volar/typescript@1.11.1(typescript@4.9.5)': + '@vitejs/plugin-react@6.1.1(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0))': dependencies: - '@volar/language-core': 1.11.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 4.9.5 + '@rolldown/pluginutils': 1.0.1 + vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) - '@vue/compiler-core@3.5.43': + '@volar/language-core@2.4.28': 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': {} - - '@xmldom/xmldom@0.9.12': {} - - abab@2.0.6: {} + typescript: 6.0.3 abbrev@1.1.1: optional: true - acorn-globals@6.0.0: - dependencies: - acorn: 7.4.1 - acorn-walk: 7.2.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): @@ -4608,18 +2859,15 @@ snapshots: debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color + optional: true - 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,48 +2876,38 @@ 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.8 + 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.8 + 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 + ansi-styles@6.2.3: {} aproba@2.1.0: optional: true - arch@2.2.0: {} + arch@3.0.0: {} are-we-there-yet@2.0.0: dependencies: @@ -4681,110 +2919,27 @@ snapshots: 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: {} - asn1@0.2.6: dependencies: safer-buffer: 2.1.2 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: + optional: true - 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: @@ -4799,31 +2954,11 @@ snapshots: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + optional: true - brace-expansion@2.1.7: - dependencies: - balanced-match: 1.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 - - 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 +2968,18 @@ 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: + 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,12 +2993,6 @@ 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): @@ -4888,93 +3005,45 @@ snapshots: - 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: {} - - 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 + colors: 1.4.0 - co@4.6.0: {} - - collect-v8-coverage@1.0.3: {} - - collection-visit@1.0.0: - dependencies: - map-visit: 1.0.0 - object-visit: 1.0.1 - - color-convert@1.9.3: + cli-truncate@5.2.0: dependencies: - color-name: 1.1.3 + slice-ansi: 8.0.0 + string-width: 8.3.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: @@ -4982,102 +3051,70 @@ snapshots: colorette@2.0.20: {} + colors@1.4.0: + optional: true + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - commander@6.2.1: {} + commander@2.11.0: {} - commander@9.5.0: - optional: true + commander@6.2.1: {} common-tags@1.8.2: {} - component-emitter@1.3.1: {} + compare-versions@6.1.1: {} + + concat-map@0.0.1: + optional: true - 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: {} - 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 - 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 +3123,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,12 +3151,6 @@ 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 @@ -5137,46 +3158,14 @@ snapshots: 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 +3180,7 @@ snapshots: electron-to-chromium@1.5.440: {} - emittery@0.7.2: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} @@ -5199,16 +3188,7 @@ 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: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 + environment@1.1.0: {} es-define-property@1.0.1: {} @@ -5225,131 +3205,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.5 + zod-validation-error: 4.0.2(zod@4.6.5) + 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 +3286,6 @@ snapshots: dependencies: estraverse: 5.3.0 - estraverse@4.3.0: {} - estraverse@5.3.0: {} estree-walker@2.0.2: {} @@ -5369,17 +3294,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 +3312,44 @@ snapshots: dependencies: pify: 2.3.0 - exit@0.1.2: {} + exsolve@1.1.1: {} - 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 + extend@3.0.2: {} - 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 + extsprintf@1.3.0: {} - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 + fast-deep-equal@3.1.3: {} - extend-shallow@3.0.2: - dependencies: - assign-symbols: 1.0.0 - is-extendable: 1.0.1 + fast-json-stable-stringify@2.1.0: {} - extend@3.0.2: {} + fast-levenshtein@2.0.6: {} - 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 + fast-uri@3.1.8: {} - 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 + fdir@6.5.0(picomatch@4.0.7): 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 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@3.0.0: - dependencies: - locate-path: 3.0.0 + 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 +3357,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: @@ -5565,7 +3375,8 @@ snapshots: minipass: 3.3.6 optional: true - fs.realpath@1.0.0: {} + fs.realpath@1.0.0: + optional: true fsevents@2.3.3: optional: true @@ -5587,7 +3398,7 @@ snapshots: 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 +3413,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,34 +3420,20 @@ 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-to-regexp@0.4.1: {} + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -5649,29 +3442,13 @@ snapshots: minimatch: 3.1.5 once: 1.4.0 path-is-absolute: 1.0.1 + optional: true 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 + globals@17.12.0: {} globrex@0.1.2: {} @@ -5681,17 +3458,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: {} @@ -5701,660 +3467,127 @@ snapshots: has-symbols: 1.1.0 has-unicode@2.0.1: - optional: true - - has-value@0.3.1: - 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: {} - - has-values@1.0.0: - dependencies: - is-number: 3.0.0 - kind-of: 4.0.0 - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - - he@1.2.0: {} - - heic2any@0.0.4: {} - - hosted-git-info@2.8.9: {} - - html-encoding-sniffer@2.0.1: - dependencies: - whatwg-encoding: 1.0.5 - - html-escaper@2.0.2: {} - - http-proxy-agent@4.0.1(supports-color@8.1.1): - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - http-signature@1.3.6: - 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): - dependencies: - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - human-signals@1.1.1: {} - - iconv-lite@0.4.24: - 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 - - jest-leak-detector@26.6.2: - dependencies: - jest-get-type: 26.3.0 - pretty-format: 26.6.2 - - 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 + optional: true - jest-matcher-utils@27.5.1: + hasha@5.2.2: dependencies: - chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 + is-stream: 2.0.1 + type-fest: 0.8.1 - jest-message-util@26.6.2: + hashery@1.5.1: 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 + hookified: 1.15.1 - jest-mock@26.6.2: + hasown@2.0.4: dependencies: - '@jest/types': 26.6.2 - '@types/node': 20.19.43 + function-bind: 1.1.2 - jest-pnp-resolver@1.2.3(jest-resolve@26.6.2): - optionalDependencies: - jest-resolve: 26.6.2 + heic2any@0.0.4: {} - jest-regex-util@26.0.0: {} + hermes-estree@0.25.1: {} - jest-resolve-dependencies@26.6.3(supports-color@8.1.1): + hermes-parser@0.25.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 + hermes-estree: 0.25.1 + + hookified@1.15.1: {} + + hookified@2.2.0: {} - jest-resolve@26.6.2: + http-signature@1.4.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 + assert-plus: 1.0.0 + jsprim: 2.0.2 + sshpk: 1.18.0 - jest-runner@26.6.3(supports-color@8.1.1): + https-proxy-agent@5.0.1(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 + agent-base: 6.0.2(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color + optional: true - 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 + human-signals@1.1.1: {} - jest-serializer@26.6.2: - dependencies: - '@types/node': 20.19.43 - graceful-fs: 4.2.11 + ieee754@1.2.1: {} - jest-snapshot@26.6.2(supports-color@8.1.1): - 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 + ignore@5.3.2: {} - jest-util@26.6.2: + ignore@7.0.10: {} + + import-lazy@4.0.0: {} + + imurmurhash@0.1.4: {} + + inflight@1.0.6: 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 + once: 1.4.0 + wrappy: 1.0.2 + optional: true + + inherits@2.0.4: + optional: true + + ini@2.0.0: {} - jest-validate@26.6.2: + is-core-module@2.17.0: 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 + hasown: 2.0.4 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} - jest-watcher@26.6.2: + is-fullwidth-code-point@5.1.0: 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 + get-east-asian-width: 1.7.0 - jest-worker@26.6.2: + is-glob@4.0.3: dependencies: - '@types/node': 20.19.43 - merge-stream: 2.0.0 - supports-color: 7.2.0 + is-extglob: 2.1.1 - jest@26.6.3(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): + is-installed-globally@0.4.0: 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 + global-dirs: 3.0.1 + is-path-inside: 3.0.3 - jju@1.4.0: {} + is-path-inside@3.0.3: {} - js-tokens@4.0.0: {} + is-stream@2.0.1: {} - js-yaml@3.15.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + is-typedarray@1.0.0: {} - js-yaml@4.3.2: - dependencies: - argparse: 2.0.1 + is-unicode-supported@0.1.0: {} - jsbn@0.1.1: {} + isexe@2.0.0: {} + + isstream@0.1.2: {} - jsdom@16.7.0(canvas@2.11.2(supports-color@8.1.1))(supports-color@8.1.1): + jest-image-snapshot@6.5.2: 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 + 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 - jsesc@3.1.0: {} + jiti@2.7.0: {} + + jju@1.4.0: {} + + js-tokens@4.0.0: {} - json-buffer@3.0.1: {} + jsbn@0.1.1: {} - json-parse-even-better-errors@2.3.1: {} + jsesc@3.1.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: {} @@ -6363,10 +3596,6 @@ snapshots: json5@2.2.3: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - jsonfile@6.2.1: dependencies: universalify: 2.0.1 @@ -6380,73 +3609,89 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 - keyv@4.5.4: + keyv@5.6.0: dependencies: - json-buffer: 3.0.1 + '@keyv/serialize': 1.1.1 - kind-of@3.2.2: - dependencies: - is-buffer: 1.1.6 + kolorist@1.8.0: {} - kind-of@4.0.0: + konva@8.4.3: {} + + levn@0.4.1: dependencies: - is-buffer: 1.1.6 + prelude-ls: 1.2.1 + type-check: 0.4.0 - kind-of@6.0.3: {} + lightningcss-android-arm64@1.33.0: + optional: true - kleur@3.0.3: {} + lightningcss-darwin-arm64@1.33.0: + optional: true - kolorist@1.8.0: {} + lightningcss-darwin-x64@1.33.0: + optional: true - konva@8.4.3: {} + lightningcss-freebsd-x64@1.33.0: + optional: true - lazy-ass@1.6.0: {} + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true - leven@3.1.0: {} + lightningcss-linux-arm64-gnu@1.33.0: + optional: true - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true - lines-and-columns@1.2.4: {} + 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.once@4.1.1: {} - lodash@4.17.23: {} - lodash@4.18.1: {} log-symbols@4.1.0: @@ -6454,21 +3699,18 @@ 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@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 @@ -6478,49 +3720,10 @@ snapshots: 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 - 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,20 +3732,23 @@ snapshots: mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + mimic-response@2.1.0: optional: true - minimatch@3.0.8: + minimatch@10.2.3: dependencies: - brace-expansion: 1.1.21 + brace-expansion: 5.0.12 - minimatch@3.1.5: + minimatch@10.2.6: dependencies: - brace-expansion: 1.1.21 + brace-expansion: 5.0.12 - minimatch@9.0.9: + minimatch@3.1.5: dependencies: - brace-expansion: 2.1.7 + brace-expansion: 1.1.21 + optional: true minimist@1.2.8: {} @@ -6560,68 +3766,30 @@ snapshots: yallist: 4.0.0 optional: true - mixin-deep@1.3.2: - dependencies: - for-in: 1.0.2 - is-extendable: 1.0.1 - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mkdirp@1.0.4: optional: true - ms@2.0.0: {} + mlly@1.8.2: + dependencies: + acorn: 8.18.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 ms@2.1.3: {} - muggle-string@0.3.1: {} - nan@2.29.0: optional: true 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 - - natural-compare-lite@1.4.1: {} - natural-compare@1.4.0: {} - nice-try@1.0.5: {} - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 optional: true - node-int64@0.4.0: {} - - node-notifier@8.0.2: - dependencies: - growly: 1.3.0 - is-wsl: 2.2.0 - semver: 7.8.5 - shellwords: 0.1.1 - uuid: 8.3.2 - which: 2.0.2 - optional: true - node-releases@2.0.57: {} nopt@5.0.0: @@ -6629,23 +3797,6 @@ snapshots: abbrev: 1.1.1 optional: true - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.12 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - - normalize-path@2.1.1: - dependencies: - remove-trailing-separator: 1.1.0 - - normalize-path@3.0.0: {} - - npm-run-path@2.0.2: - dependencies: - path-key: 2.0.1 - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -6658,27 +3809,11 @@ snapshots: set-blocking: 2.0.0 optional: true - nwsapi@2.2.28: {} - object-assign@4.1.1: optional: true - object-copy@0.1.0: - dependencies: - copy-descriptor: 0.1.1 - define-property: 0.2.5 - kind-of: 3.2.2 - object-inspect@1.13.4: {} - object-visit@1.0.1: - dependencies: - isobject: 3.0.1 - - object.pick@1.3.0: - dependencies: - isobject: 3.0.1 - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -6687,6 +3822,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,72 +3837,32 @@ 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: {} - pako@1.0.11: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - 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: {} - 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-is-absolute@1.0.1: + optional: true path-key@3.1.1: {} path-parse@1.0.7: {} - path-type@4.0.0: {} - path2d-polyfill@2.0.1: optional: true + pathe@2.0.3: {} + pdfjs-dist@3.11.174(supports-color@8.1.1): optionalDependencies: canvas: 2.11.2(supports-color@8.1.1) @@ -6778,38 +3877,30 @@ 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: - dependencies: - find-up: 3.0.0 - - pkg-dir@4.2.0: + pkg-types@1.3.1: dependencies: - find-up: 4.1.0 + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 - plist@3.1.1: + pkg-types@2.3.3: 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@8.5.28: dependencies: nanoid: 3.3.19 @@ -6818,40 +3909,14 @@ snapshots: 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: {} - - 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 + prettier@3.9.9: {} - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 + pretty-bytes@5.6.0: {} process@0.11.10: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - proxy-from-env@1.0.0: {} - psl@1.15.0: - dependencies: - punycode: 2.3.1 - pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -6859,38 +3924,24 @@ 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 @@ -6898,45 +3949,11 @@ snapshots: 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: {} - 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 +3962,65 @@ 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: + rimraf@3.0.2: dependencies: glob: 7.2.3 + optional: true - rimraf@3.0.2: + 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 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 + set-blocking@2.0.0: + optional: true 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,6 +4051,8 @@ snapshots: signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + simple-concat@1.0.1: optional: true @@ -7086,86 +4063,20 @@ snapshots: simple-concat: 1.0.1 optional: true - sisteransi@1.0.5: {} - - slash@3.0.0: {} - - slice-ansi@3.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - snapdragon-node@2.1.1: - dependencies: - define-property: 1.0.0 - isobject: 3.0.1 - snapdragon-util: 3.0.1 - - snapdragon-util@3.0.1: + slice-ansi@7.1.2: dependencies: - kind-of: 3.2.2 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 - snapdragon@0.8.2(supports-color@8.1.1): + slice-ansi@8.0.0: 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 - transitivePeerDependencies: - - supports-color + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 source-map-js@1.2.1: {} - source-map-resolve@0.5.3: - 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: {} - - source-map@0.5.7: {} - source-map@0.6.1: {} - source-map@0.7.6: {} - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.24 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.24 - - spdx-license-ids@3.0.24: {} - - split-string@3.1.0: - dependencies: - extend-shallow: 3.0.2 - sprintf-js@1.0.3: {} sshpk@1.18.0: @@ -7182,55 +4093,40 @@ snapshots: ssim.js@3.5.0: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - - static-extend@0.1.2: - dependencies: - define-property: 0.2.5 - object-copy: 0.1.0 - 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-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.7.0 + strip-ansi: 7.2.0 + + string-width@8.3.0: + dependencies: + get-east-asian-width: 1.7.0 + strip-ansi: 7.2.0 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 optional: true - strip-ansi@3.0.1: - dependencies: - ansi-regex: 2.1.1 - 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,14 +4135,9 @@ 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: {} + systeminformation@5.33.14: {} tar@6.2.1: dependencies: @@ -7258,91 +4149,37 @@ snapshots: 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 - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 7.2.3 - minimatch: 3.1.5 - - text-table@0.2.0: {} - - throat@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 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 - to-regex-range@2.1.1: - dependencies: - is-number: 3.0.0 - repeat-string: 1.6.1 + tldts-core@6.1.86: {} - to-regex-range@5.0.1: + tldts@6.1.86: dependencies: - is-number: 7.0.0 + tldts-core: 6.1.86 - 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 + tmp@0.2.7: {} - tough-cookie@4.1.4: + tough-cookie@5.1.2: dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 + tldts: 6.1.86 tr46@0.0.3: optional: true - tr46@2.1.0: - dependencies: - punycode: 2.3.1 + tree-kill@1.2.2: {} - ts-patch@1.4.5(typescript@4.9.5): + ts-api-utils@2.5.0(typescript@6.0.3): 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 + typescript: 6.0.3 - tsconfck@3.1.6(typescript@4.9.5): + tsconfck@3.1.6(typescript@6.0.3): optionalDependencies: - typescript: 4.9.5 - - tslib@1.14.1: {} - - tslib@2.8.1: {} - - tsutils@3.21.0(typescript@4.9.5): - dependencies: - tslib: 1.14.1 - typescript: 4.9.5 + typescript: 6.0.3 tunnel-agent@0.6.0: dependencies: @@ -7354,48 +4191,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-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-transform-paths@3.5.6(typescript@4.9.5): - dependencies: - minimatch: 9.0.9 - typescript: 4.9.5 + typescript@5.9.3: {} - typescript@4.9.5: {} + typescript@6.0.3: {} - typescript@5.4.2: {} + ufo@1.6.4: {} undici-types@6.21.0: {} - 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 - - universalify@0.1.2: {} - - universalify@0.2.0: {} + undici-types@7.24.6: {} 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.1(@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.1(@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,15 +4255,6 @@ 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 @@ -7427,98 +4264,56 @@ snapshots: 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 - - validator@13.15.35: {} - verror@1.10.0: dependencies: assert-plus: 1.0.0 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)): + 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.1(@types/node@24.19.0)(jiti@2.7.0)): 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) + 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.1(@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.1(@types/node@24.19.0)(jiti@2.7.0) transitivePeerDependencies: - - '@types/node' - - rollup + - '@rspack/core' + - '@vue/language-core' + - esbuild + - rolldown - supports-color + - typescript + - webpack - 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-tsconfig-paths@6.1.1(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.1(@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) - optionalDependencies: - vite: 4.5.14(@types/node@20.19.43) + tsconfck: 3.1.6(typescript@6.0.3) + vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) transitivePeerDependencies: - supports-color - typescript - vite@4.5.14(@types/node@20.19.43): + vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0): dependencies: - esbuild: 0.18.20 + lightningcss: 1.33.0 + picomatch: 4.0.7 postcss: 8.5.28 - rollup: 3.30.0 + rolldown: 1.2.11 + 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 + vscode-uri@3.2.0: {} 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: {} + webpack-virtual-modules@0.6.2: {} whatwg-url@5.0.0: dependencies: @@ -7526,18 +4321,6 @@ snapshots: 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: {} - - which@1.3.1: - dependencies: - isexe: 2.0.0 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -7549,71 +4332,29 @@ snapshots: 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: - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 - 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@4.0.0: + optional: true - 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.5): dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.15.35 - optionalDependencies: - commander: 9.5.0 + zod: 4.6.5 + + zod@4.6.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index be73870..e423dbd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ allowBuilds: + # optional dep of pdfjs-dist 3.x (Node only); not needed in the browser canvas: false cypress: true esbuild: true diff --git a/src/common/constants.ts b/src/common/constants.ts index 1418057..1b9170a 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,6 +1,6 @@ -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, @@ -37,7 +37,7 @@ export const DEFAULT_ANNOTATION_VIEWER_OPTIONS = { layer?.batchDraw() }, onMouseEnter: (polygon: Line) => { - const stroke = polygon.getAttr('stroke') + const stroke = polygon.stroke() polygon.setAttr('fill', `${stroke}40`) polygon.draw() }, diff --git a/src/common/types.ts b/src/common/types.ts index ce2b5fa..ccb2296 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,11 +1,10 @@ -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 & { +export type AnnotationShape = T & { id: string coordinates: number[][] config?: LineConfig @@ -13,11 +12,11 @@ export type AnnotationShape = T & { export type Orientation = 0 | 90 | 180 | 270 -export type BaseOptions = { +export interface BaseOptions { shapeConfig?: LineConfig } -export type AnnotationLayers = { +export interface AnnotationLayers { shapes: Konva.Layer image: Konva.Layer } @@ -37,7 +36,7 @@ export type AnnotationViewerOptions = BaseOptions & { } } -export type ImageBoundingBox = { +export interface ImageBoundingBox { x: number y: number width: number @@ -45,23 +44,23 @@ export type ImageBoundingBox = { scale: number } -export type PointerPosition = { +export interface PointerPosition { x: number y: number } -export type AnnotationData = { +export interface AnnotationData { image?: string | null shapes?: AnnotationShape[] orientation?: Orientation } -export type ImageData = { +export interface ImageData { element: HTMLImageElement shape: Konva.Image } -export type ZoomOptions = { +export interface ZoomOptions { scale?: number position?: PointerPosition } diff --git a/src/components/AnnotationLens.spec.tsx b/src/components/AnnotationLens.spec.tsx index 2379acc..37d6eaa 100644 --- a/src/components/AnnotationLens.spec.tsx +++ b/src/components/AnnotationLens.spec.tsx @@ -3,14 +3,14 @@ import anotherDummyImage from 'cypress/assets/another-demo.jpg' import dummyImage from 'cypress/assets/demo.jpg' import { dummyShapes } from 'cypress/assets/shapes' -import { AnnotationData } from '@/common/types' +import type { AnnotationData } from '@/common/types' import AnnotationLens from './AnnotationLens' const containerHeight = 600 const containerWidth = 600 -type TesterProps = { +interface TesterProps { containerWidth: number containerHeight: number id?: string diff --git a/src/components/AnnotationLens.tsx b/src/components/AnnotationLens.tsx index 03d743c..f6c855b 100644 --- a/src/components/AnnotationLens.tsx +++ b/src/components/AnnotationLens.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useRef } from 'react' +import React, { useEffect, useId, useRef, useState } from 'react' import Konva from 'konva' -import { v4 as uuidv4 } from 'uuid' import { DEFAULT_ANNOTATION_LENS_OPTIONS, @@ -10,8 +9,10 @@ import { DEFAULT_STYLE, KONVA_REFS, } from '@/common/constants' -import { +import type { + AnnotationLensOptions, AnnotationLensProps, + AnnotationShape, ImageBoundingBox, ImageData, } from '@/common/types' @@ -22,8 +23,20 @@ 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, +) => { + 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, @@ -31,114 +44,105 @@ export default function AnnotationLens({ options: customOptions = {}, data = DEFAULT_DATA, }: AnnotationLensProps) { - const options = { + 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 layersObject = useRef({ shapes: new Konva.Layer({ id: KONVA_REFS.shapesLayer, listening: false }), image: new Konva.Layer({ listening: false }), }) const stageObject = useRef(null) const imageBoundingBoxObject = useRef(null) + const [imageVersion, setImageVersion] = useState(0) + + const getStageRef = useRef(getStage) useEffect(() => { - stageObject.current = new Konva.Stage({ - container: containerId, - listening: false, - }) - getStage?.(stageObject.current) - stageObject.current.add( - layersObject.current.image, - layersObject.current.shapes, - ) + getStageRef.current = getStage + }) + + useEffect(() => { + const container = containerRef.current + if (!container) return + 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 onZoomChange = () => { - handleLensZoom( - stageObject.current, - imageBoundingBoxObject.current, - pointerPosition, - zoomLevel, - ) - } - - useEffect(() => { - onZoomChange() - }, [pointerPosition, zoomLevel]) + const { image, orientation, shapes } = data useEffect(() => { - if (!data.image) { + if (!image) { clearLayers(layersObject.current) imageDataObject.current.element = new Image() imageDataObject.current.shape.image(imageDataObject.current.element) - annotationData.current = data + imageBoundingBoxObject.current = null return } - if ( - annotationData.current.image !== data.image || - data.orientation !== annotationData.current.orientation - ) { - annotationData.current = data - loadImage() - } else { - annotationData.current = data - drawShapes() - } - }, [data]) - - const loadImage = async () => { - imageDataObject.current.element.onload = () => { - imageDataObject.current.shape.image(imageDataObject.current.element) - resizeImage() - } - 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 src = orientation ? await rotateImage(image, orientation) : image + 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, + pointerPosition, + zoomLevel, ) + }, [pointerPosition, zoomLevel, imageVersion]) - layersObject.current.shapes.batchDraw() - } - - const resizeImage = () => { - const imageBoundingBox = handleResizeImage( - stageObject.current, - containerRef.current, - imageDataObject.current, + useEffect(() => { + drawLensShapes( + layersObject.current.shapes, + shapes, + imageBoundingBoxObject.current, + optionsRef.current, ) - if (imageBoundingBox) { - imageBoundingBoxObject.current = imageBoundingBox - onZoomChange() - drawShapes() - } - } + }, [shapes, optionsKey, imageVersion]) return (
{ it('shows an JPG image in the canvas', () => { cy.mount( { it('shows a TIFF image in the canvas', () => { cy.mount( { it('shows a HEIC image in the canvas', () => { cy.mount( { it('shows a remote image in the canvas', () => { cy.mount( { }) 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 - }) - 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', () => { diff --git a/src/components/AnnotationViewer.tsx b/src/components/AnnotationViewer.tsx index 1392418..984c40d 100644 --- a/src/components/AnnotationViewer.tsx +++ b/src/components/AnnotationViewer.tsx @@ -1,7 +1,5 @@ -import React, { useEffect, useRef } from 'react' +import React, { useEffect, useId, useRef, useState } from 'react' import Konva from 'konva' -import { KonvaEventObject } from 'konva/lib/Node' -import { v4 as uuidv4 } from 'uuid' import { DEFAULT_ANNOTATION_VIEWER_OPTIONS, @@ -9,7 +7,9 @@ import { DEFAULT_STYLE, KONVA_REFS, } from '@/common/constants' -import { +import type { + AnnotationShape, + AnnotationViewerOptions, AnnotationViewerProps, ImageBoundingBox, ImageData, @@ -30,10 +30,47 @@ import { onSelectionStart, } from '@/utils/selection' import useMultiSelection from '@/utils/useMultiSelection' -import { handleStageZoom, handleZoomScale } from '@/utils/zoom' +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, +) => { + shapesLayer.destroyChildren() + if (options.enableSelection) { + 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, @@ -46,11 +83,13 @@ export default function AnnotationViewer({ options: customOptions = {}, data = DEFAULT_DATA, }: AnnotationViewerProps) { - const options = { + const generatedId = useId() + const containerId = id ?? generatedId + const options: AnnotationViewerOptions = { ...DEFAULT_ANNOTATION_VIEWER_OPTIONS, ...customOptions, } - const annotationData = useRef(DEFAULT_DATA) + const optionsKey = JSON.stringify(options) const isSelectionActiveRef = useRef(false) const containerRef = useRef(null) const selectionRectObject = useRef(createSelectionRect(options)) @@ -64,196 +103,193 @@ export default function AnnotationViewer({ }) const stageObject = useRef(null) const imageBoundingBoxObject = useRef(null) + const [imageVersion, setImageVersion] = useState(0) - useMultiSelection({ stage: stageObject.current, isSelectionActiveRef }) - - useEffect(() => { - initCanvas() - }, []) - + const optionsRef = useRef(options) + const callbacksRef = useRef({}) useEffect(() => { - window.addEventListener('resize', resizeImage) - return () => { - window.removeEventListener('resize', resizeImage) - } - }, [data]) - - 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, - }) + optionsRef.current = options + callbacksRef.current = { + getPointerPosition, + getStage, + onShapeClick, + onShapeMouseEnter, + onShapeMouseLeave, + onShapeMultiSelect, } - }, [customStagePosition]) + }) - const applyCustomZoomLevel = () => { - if (!customZoomLevel) return + useMultiSelection({ stageRef: stageObject, isSelectionActiveRef }) - handleZoomScale( + const fitImageToContainer = () => { + const imageBoundingBox = handleResizeImage( stageObject.current, - customZoomLevel, - imageBoundingBoxObject.current, + containerRef.current, + imageDataObject.current, ) + if (!imageBoundingBox) return + imageBoundingBoxObject.current = imageBoundingBox + layersObject.current.image.batchDraw() + setImageVersion((v) => v + 1) } - useEffect(() => applyCustomZoomLevel(), [customZoomLevel]) - 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]) + const container = containerRef.current + if (!container) 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) - const initCanvas = () => { - stageObject.current = new Konva.Stage({ - container: containerId, + stage.on('wheel', (event) => { + handleStageZoom( + stage, + imageBoundingBoxObject.current, + event, + optionsRef.current, + ) }) - 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, - ), + stage.on('mousemove', () => { + const { getPointerPosition } = callbacksRef.current + if (!getPointerPosition) return + const mousePointTo = getMousePosition( + stage, + imageBoundingBoxObject.current, ) - stageObject.current.on('mousemove touchmove', () => - onSelectionMove( - layersObject.current.shapes, - selectionRectObject.current, - ), + if (mousePointTo) getPointerPosition(mousePointTo) + }) + stage.on('mousedown touchstart', (event) => { + if (!optionsRef.current.enableSelection) return + onSelectionStart( + event, + layers.shapes, + selectionRectObject.current, + isSelectionActiveRef.current, ) - stageObject.current.on('mouseup touchend', () => - onSelectionEnd( - layersObject.current.shapes, - selectionRectObject.current, - onShapeMultiSelect, - ), + }) + stage.on('mousemove touchmove', () => { + if (!optionsRef.current.enableSelection) return + onSelectionMove(layers.shapes, selectionRectObject.current) + }) + stage.on('mouseup touchend', () => { + if (!optionsRef.current.enableSelection) return + onSelectionEnd( + layers.shapes, + selectionRectObject.current, + callbacksRef.current.onShapeMultiSelect, ) + }) + stage.on('dragstart', () => { + if (!optionsRef.current.enableSelection) return + stage.container().style.cursor = 'grabbing' + }) + stage.on('dragend', () => { + if (!optionsRef.current.enableSelection) return + stage.container().style.cursor = 'pointer' + }) - 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' - } - }) + return () => { + stage.destroy() + stageObject.current = null } - } + }, []) - 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(() => { + window.addEventListener('resize', fitImageToContainer) + return () => { + window.removeEventListener('resize', fitImageToContainer) } + }, []) - if (annotationData.current.orientation) { - try { - const image = await rotateImage(annotationData.current) + const { image, orientation, shapes } = data - imageDataObject.current.element.src = image + useEffect(() => { + if (!image) { + clearLayers(layersObject.current) + imageDataObject.current.element = new Image() + imageDataObject.current.shape.image(imageDataObject.current.element) + imageBoundingBoxObject.current = null + return + } + const controller = new AbortController() + const { signal } = controller + void (async () => { + try { + const prepared = await prepareImage(image) + signal.throwIfAborted() + const src = orientation + ? await rotateImage(prepared, orientation) + : prepared + 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! - } - } - - const drawShapes = () => { - layersObject.current.shapes.destroyChildren() - if (options.enableSelection) { - layersObject.current.shapes.add(selectionRectObject.current) - } - if (!annotationData.current.shapes) { - return + })() + return () => { + controller.abort() } + }, [image, orientation]) - mapShapesToPolygons( + useEffect(() => { + drawViewerShapes( layersObject.current.shapes, - annotationData.current.shapes, - true, + shapes, imageBoundingBoxObject.current, - options, - onShapeClick, - onShapeMouseEnter, - onShapeMouseLeave, + { + ...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), + }, ) - layersObject.current.shapes.batchDraw() - } + }, [shapes, optionsKey, imageVersion]) - const resizeImage = () => { - const imageBoundingBox = handleResizeImage( - stageObject.current, - containerRef.current, - imageDataObject.current, - ) - if (imageBoundingBox) { - imageBoundingBoxObject.current = imageBoundingBox - layersObject.current.image.batchDraw() - drawShapes() - } - } - - const onZoom = (event: KonvaEventObject) => { - handleStageZoom( + useEffect(() => { + if (!customZoomLevel) return + 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 (
stage.getAttr('zoomScale') - -type AnnotationViewerWithDynamicZoomProps = { +interface AnnotationViewerWithDynamicZoomProps { id: AnnotationViewerProps['id'] data: AnnotationViewerProps['data'] } @@ -26,9 +26,9 @@ const AnnotationViewerWithDynamicZoom = ({ 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) => { @@ -40,33 +40,51 @@ const AnnotationViewerWithDynamicZoom = ({ data-cy="AnnotationViewerWithDynamicZoom" style={{ display: 'flex', flexDirection: 'column' }} > - - diff --git a/src/index.ts b/src/index.ts index a072489..3f5ef59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -import { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage' -import { +import type { AnnotationData, AnnotationLensOptions, AnnotationShape, @@ -20,6 +20,7 @@ import { import getImagesFromPDF from './utils/getImagesFromPDF' import getPDFPageCount from './utils/getPDFPageCount' import { dataURItoBlob } from './utils/image' +import { getZoomScale } from './utils/zoom' export type { Stage, @@ -38,6 +39,7 @@ export { AnnotationViewer, getImagesFromPDF, getPDFPageCount, + getZoomScale, drawShape, drawLayer, setShapeConfig, diff --git a/src/utils/canvas.spec.ts b/src/utils/canvas.spec.ts index 98b3ba7..6c244a2 100644 --- a/src/utils/canvas.spec.ts +++ b/src/utils/canvas.spec.ts @@ -1,6 +1,6 @@ -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' @@ -45,7 +45,7 @@ describe('canvas', () => { const actual = getMousePosition( stage as unknown as Stage, - imageBoundingBox as ImageBoundingBox, + imageBoundingBox, ) expect(actual).to.deep.equal(expected) diff --git a/src/utils/canvas.ts b/src/utils/canvas.ts index 6622f8a..0989eb3 100644 --- a/src/utils/canvas.ts +++ b/src/utils/canvas.ts @@ -1,9 +1,9 @@ import Konva from 'konva' -import { Layer } from 'konva/lib/Layer' -import { Line } from 'konva/lib/shapes/Line' +import type { Layer } from 'konva/lib/Layer' +import type { Line } from 'konva/lib/shapes/Line' import { KONVA_REFS } from '@/common/constants' -import { +import type { AnnotationLensOptions, AnnotationShape, AnnotationViewerOptions, @@ -13,6 +13,9 @@ import { import { roundTo } from './roundTo' +const shapeByNode = new WeakMap() +export const getShapeFromNode = (node: Konva.Node) => shapeByNode.get(node) + export const mapShapesToPolygons = ( shapesLayer: Layer, shapes: AnnotationShape[] = [], @@ -33,15 +36,15 @@ export const mapShapesToPolygons = ( points: mapCoordinatesToPoints(shape.coordinates, imageBoundingBox), closed: true, - ...(options?.shapeConfig || {}), + ...(options.shapeConfig ?? {}), ...shape.config, - shape, }) + shapeByNode.set(polygon, shape) shapesLayer.add(polygon) if (useEvents) { bindEventToPolygon( polygon, - options as AnnotationViewerOptions, + options, onClick, onShapeMouseEnter, onShapeMouseLeave, @@ -58,22 +61,23 @@ const bindEventToPolygon = ( onShapeMouseLeave?: (shape: AnnotationShape) => void, ) => { const stage = polygon.getStage() - const shape = polygon.getAttr('shape') + const shape = shapeByNode.get(polygon) + if (!shape || !stage) return polygon.on('mouseup', (event) => { event.cancelBubble = true onClick?.(shape) - options?.onClick?.(polygon) + options.onClick?.(polygon) }) polygon.on('mouseleave', function (event) { event.cancelBubble = true - stage!.container().style.cursor = 'inherit' - options?.onMouseLeave?.(polygon) + 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' + options.onMouseEnter?.(polygon) + stage.container().style.cursor = 'pointer' onShapeMouseEnter?.(shape) }) } @@ -109,7 +113,7 @@ export const getMousePosition = ( if (!stage || !imageBoundingBox) { return } - const { x: pointerX, y: pointerY } = stage.getPointerPosition() || { + const { x: pointerX, y: pointerY } = stage.getPointerPosition() ?? { x: 0, y: 0, } diff --git a/src/utils/functions.ts b/src/utils/functions.ts index b2cbe8d..bbcb79a 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,14 +1,16 @@ import Konva from 'konva' -import { Layer } from 'konva/lib/Layer' -import { LineConfig } from 'konva/lib/shapes/Line' +import type { LineConfig } from 'konva/lib/shapes/Line' +import type { Stage } from 'konva/lib/Stage' import { KONVA_REFS } from '@/common/constants' -import { Stage } from '..' +/** Konva 8 types `findOne` as non-optional although it returns undefined on miss. */ +const findOne = (stage: Konva.Stage, selector: string) => + stage.findOne(selector) as Konva.Node | undefined export const drawLayer = (stage: Konva.Stage) => { - const shapesLayer = stage.findOne(`#${KONVA_REFS.shapesLayer}`) - if (shapesLayer) { + const shapesLayer = findOne(stage, `#${KONVA_REFS.shapesLayer}`) + if (shapesLayer instanceof Konva.Layer) { shapesLayer.batchDraw() } else { console.error('drawLayer : the layer is not found') @@ -20,8 +22,8 @@ export const drawShape = ( id: string | number, config: LineConfig, ) => { - const shape = stage.findOne(`#${id}`) - if (shape) { + const shape = findOne(stage, `#${id.toString()}`) + if (shape instanceof Konva.Shape) { shape.setAttrs(config) shape.draw() } else { @@ -44,7 +46,7 @@ export const setShapeConfig = ( id: string | number, config: LineConfig, ) => { - const shape = stage.findOne(`#${id}`) + const shape = findOne(stage, `#${id.toString()}`) if (shape) { shape.setAttrs(config) } else { diff --git a/src/utils/getImagesFromPDF.spec.tsx b/src/utils/getImagesFromPDF.spec.tsx index 8edd2c1..8cf12d4 100644 --- a/src/utils/getImagesFromPDF.spec.tsx +++ b/src/utils/getImagesFromPDF.spec.tsx @@ -11,14 +11,14 @@ describe('getImageFromPDF', () => { }) 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..b9b75bb 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -1,11 +1,5 @@ -import { - getDocument, - GlobalWorkerOptions, - PDFDocumentProxy, - PDFPageProxy, - version, -} from 'pdfjs-dist' -import { RenderParameters } from 'pdfjs-dist/types/src/display/api' +import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist' +import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist' import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants' @@ -19,6 +13,9 @@ const getImageFromPage = async ( const page: PDFPageProxy = await _document.getPage(pageNumber) const canvas = document.createElement('canvas') const context = canvas.getContext('2d') + if (!context) { + throw new Error('Could not acquire a 2D canvas context') + } const [, , width, height] = page.view const newScale = (resolution / (height * width)) ** (1 / 2) const safeScale = Math.min(newScale, MAX_PDF_SCALE) @@ -29,50 +26,28 @@ const getImageFromPage = async ( canvasContext: context, viewport: viewport, } - return page - .render(renderContext as RenderParameters) - .promise.then(() => canvas.toDataURL()) - .catch((error) => { - throw new Error(error) - }) + return page.render(renderContext).promise.then(() => canvas.toDataURL()) } -export default function getImagesFromPDF( +export default async function getImagesFromPDF( file: string, 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 getDocument(file).promise + if (pdf.numPages > maxPages) { + const error = new Error('Too many pages') + error.name = 'TooManyPagesError' + throw error + } + onSuccess?.() + const results = await Promise.allSettled( + Array.from({ length: pdf.numPages }, (_, index) => + getImageFromPage(pdf, index + 1, resolution), + ), + ) + return results.flatMap((result) => + result.status === 'fulfilled' ? [result.value] : [], + ) } diff --git a/src/utils/getPDFPageCount.spec.tsx b/src/utils/getPDFPageCount.spec.tsx index 04f1223..ce7961a 100644 --- a/src/utils/getPDFPageCount.spec.tsx +++ b/src/utils/getPDFPageCount.spec.tsx @@ -11,8 +11,8 @@ describe('getPDFPageCount', () => { }) 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..63212e7 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,16 +1,8 @@ -import { - getDocument, - GlobalWorkerOptions, - PDFDocumentProxy, - version, -} from 'pdfjs-dist' +import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist' 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)) - }) +export default async function getPDFPageCount(file: string) { + const pdf = await getDocument(file).promise + return pdf.numPages } diff --git a/src/utils/image.spec.ts b/src/utils/image.spec.ts index 635d965..8b261cb 100644 --- a/src/utils/image.spec.ts +++ b/src/utils/image.spec.ts @@ -1,6 +1,6 @@ -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 { computeImageBoundingBox, diff --git a/src/utils/image.ts b/src/utils/image.ts index bab64f7..18ec5a9 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,16 +1,20 @@ import heic2any from 'heic2any' -import Konva from 'konva' +import type Konva from 'konva' +import type { Stage } from 'konva/lib/Stage' import UTIF from 'utif' -import { ImageData } from '@/common/types' +import type { + ImageBoundingBox, + ImageData, + PointerPosition, +} from '@/common/types' -import { Stage } from '..' -import { ImageBoundingBox, PointerPosition } from './../common/types' +import { getZoomScale, setZoomScale } from '@/utils/zoom' export const dataURItoBlob = (dataURI: string) => { let byteString const splitDataURL = dataURI.split(',') - if (splitDataURL[0].indexOf('base64') >= 0) { + if (splitDataURL[0].includes('base64')) { // atob decodes base64 data byteString = atob(splitDataURL[1]) } else { @@ -59,7 +63,10 @@ export const tiffToJpg = async (blob: Blob) => { cnv.width = imageWidth cnv.height = imageHeight - const ctx = cnv.getContext('2d')! + const ctx = cnv.getContext('2d') + if (!ctx) { + throw new Error('Could not get 2D context') + } const imageData = ctx.createImageData(imageWidth, imageHeight) for (let i = 0; i < rgba.length; i++) { imageData.data[i] = rgba[i] @@ -70,7 +77,7 @@ export const tiffToJpg = async (blob: Blob) => { if (blob) { resolve(URL.createObjectURL(blob)) } else { - reject('Error getting Blob') + reject(new Error('Could not encode TIFF canvas to a Blob')) } }) }) @@ -103,7 +110,9 @@ export const computeImageBoundingBox = ( } 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), @@ -127,11 +136,11 @@ export const handleResizeImage = ( resizeStage(stage, container) const imageBoundingBox = computeImageBoundingBox(container, element) const { x, y, width, height, scale } = imageBoundingBox - stage?.scale({ + stage.scale({ x: scale, y: scale, }) - stage.setAttr('zoomScale', 1) + setZoomScale(stage, 1) stage.position({ x, y }) shape.width(width / scale) shape.height(height / scale) @@ -148,7 +157,7 @@ export const setStageBasedImagePosition = ({ newPosition: PointerPosition }) => { const { x, y, width, height } = imageBoundingBox - const zoomScale = stage.getAttr('zoomScale') + const zoomScale = getZoomScale(stage) let stageX = stage.x() let stageY = stage.y() const { x: newStageX, y: newStageY } = newPosition diff --git a/src/utils/layer.ts b/src/utils/layer.ts index cdad1f0..62d3ccd 100644 --- a/src/utils/layer.ts +++ b/src/utils/layer.ts @@ -1,4 +1,4 @@ -import { AnnotationLayers } from '@/common/types' +import type { AnnotationLayers } from '@/common/types' export const clearLayers = (layers: AnnotationLayers) => { layers.shapes.destroyChildren() diff --git a/src/utils/orientation.ts b/src/utils/orientation.ts index fcbe5ea..22ad87c 100644 --- a/src/utils/orientation.ts +++ b/src/utils/orientation.ts @@ -1,39 +1,31 @@ -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 - } - return applyRotation(image as string, orientation) +const EXIF_ORIENTATION: Record = { + 0: 1, + 90: 6, + 180: 3, + 270: 8, } +export const rotateImage = (image: string, degrees: Orientation = 0) => + applyRotation(image, EXIF_ORIENTATION[degrees]) + const applyRotation = (file: string, orientation: number) => - new Promise((resolve) => { + 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 context = canvas.getContext('2d', { alpha: false, - }) as CanvasRenderingContext2D + }) + if (!context) { + reject(new Error('Could not acquire a 2D canvas context')) + return + } const { width, height } = image const [outputWidth, outputHeight] = @@ -90,9 +82,13 @@ const applyRotation = (file: string, orientation: number) => 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( + outputContext.drawImage( canvas, sx, sy, diff --git a/src/utils/selection.spec.ts b/src/utils/selection.spec.ts index fa11062..ff9e382 100644 --- a/src/utils/selection.spec.ts +++ b/src/utils/selection.spec.ts @@ -1,4 +1,4 @@ -import { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage' import { calculateSelectionPoint } from '@/utils/selection' diff --git a/src/utils/selection.ts b/src/utils/selection.ts index 80e35b9..ad34a3e 100644 --- a/src/utils/selection.ts +++ b/src/utils/selection.ts @@ -1,15 +1,22 @@ 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 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 type { + AnnotationShape, + AnnotationViewerOptions, + PointerPosition, +} from '@/common/types' + +import { getShapeFromNode } from '@/utils/canvas' import { roundTo } from './roundTo' +const selectionAnchor = new WeakMap() + export const createSelectionRect = (options: AnnotationViewerOptions) => new Konva.Rect({ visible: false, @@ -41,6 +48,7 @@ export const onSelectionStart = ( if (!firstPoint) return + selectionAnchor.set(rect, firstPoint) rect.setAttrs({ x1: firstPoint.x, y1: firstPoint.y }) rect.visible(true) rect.width(0) @@ -55,17 +63,18 @@ export const onSelectionMove = (layer?: Layer, rect?: Rect) => { // no nothing if we didn't start selection if (!rect.visible()) return + const anchor = selectionAnchor.get(rect) + + if (!anchor) return - const { x1, y1 } = rect.getAttrs() 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() @@ -89,12 +98,13 @@ export const onSelectionEnd = ( layer.batchDraw() }) - const shapes = (stage.find(`.${KONVA_REFS.shape}`) || []) as Line[] + const shapes = stage.find(`.${KONVA_REFS.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) diff --git a/src/utils/useEventListener.ts b/src/utils/useEventListener.ts index 8db8d4f..a572db3 100644 --- a/src/utils/useEventListener.ts +++ b/src/utils/useEventListener.ts @@ -1,51 +1,23 @@ -import { RefObject, useEffect, useRef } from 'react' +import { useEffect, useRef, type RefObject } from 'react' -export default function useEventListener< - T extends HTMLElement = HTMLDivElement, - E = Event, ->( - eventName: string, - - handler: (event: E) => void, - - element?: RefObject, +export default function useEventListener( + eventName: K, + handler: (event: WindowEventMap[K]) => void, + element?: RefObject, ) { - // Create a ref that stores handler - - const savedHandler = useRef<(event: E) => void>(null) - + 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 + savedHandler.current = handler + }) - 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) - } + useEffect(() => { + const target: HTMLElement | Window = element?.current ?? window + const listener = (event: Event) => { + savedHandler.current(event as WindowEventMap[K]) } - // @ts-expect-error - targetElement.addEventListener(eventName, eventListener) - - // Remove event listener on cleanup - + target.addEventListener(eventName, listener) return () => { - // @ts-expect-error - targetElement.removeEventListener(eventName, eventListener) + target.removeEventListener(eventName, listener) } - }, [eventName, element, handler]) + }, [eventName, element]) } diff --git a/src/utils/useMultiSelection.ts b/src/utils/useMultiSelection.ts index b58d21b..d4db99f 100644 --- a/src/utils/useMultiSelection.ts +++ b/src/utils/useMultiSelection.ts @@ -1,44 +1,35 @@ -import { MutableRefObject } from 'react' -import Konva from 'konva' +import type { RefObject } from 'react' +import type Konva from 'konva' import useEventListener from './useEventListener' interface Props { - stage: Konva.Stage | null - isSelectionActiveRef: MutableRefObject + stageRef: RefObject + isSelectionActiveRef: RefObject } + +const isSelectionModifier = (event: KeyboardEvent) => + event.ctrlKey || + event.altKey || + event.key === 'Control' || + event.key === 'Alt' + 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 - } - }, - ) + useEventListener('keydown', (event) => { + event.stopPropagation() + if (isSelectionModifier(event)) { + stageRef.current?.draggable(false) + isSelectionActiveRef.current = true + } + }) + useEventListener('keyup', (event) => { + event.stopPropagation() + if (isSelectionModifier(event)) { + stageRef.current?.draggable(true) + isSelectionActiveRef.current = false + } + }) } diff --git a/src/utils/zoom.spec.ts b/src/utils/zoom.spec.ts index bb8ecbd..dab5a05 100644 --- a/src/utils/zoom.spec.ts +++ b/src/utils/zoom.spec.ts @@ -1,4 +1,6 @@ -import Konva from 'konva' +import type Konva from 'konva' + +import type { AnnotationViewerOptions } from '@/common/types' import { calculateLensZoom, @@ -6,8 +8,6 @@ import { calculateZoomScale, } from '@/utils/zoom' -import { AnnotationViewerOptions } from '..' - describe('zoom', () => { describe('calculateLensZoom', () => { it('should return the correct position', () => { diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts index 64e6a6d..563862d 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -1,8 +1,8 @@ -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 type { AnnotationViewerOptions, ImageBoundingBox, PointerPosition, @@ -10,6 +10,18 @@ import { 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) => { + stage.setAttr(ZOOM_SCALE_ATTR, zoomScale) +} + export const calculateZoomScale = ( stage: Konva.Stage, zoomScale: number, @@ -55,11 +67,11 @@ export const handleZoomScale = ( stage.draggable(false) stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }) stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }) - stage.setAttr('zoomScale', 1) + setZoomScale(stage, 1) } else { stage.draggable(true) stage.scale({ x: newScale, y: newScale }) - stage.setAttr('zoomScale', newScale / imageBoundingBox.scale) + setZoomScale(stage, newScale / imageBoundingBox.scale) stage.position(newPos) } @@ -74,14 +86,15 @@ export const calculateStageZoom = ( const oldScale = stage.scaleX() const stagePosition = stage.position() - const pointerPosition = stage.getPointerPosition() || { x: 0, y: 0 } + 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 @@ -99,17 +112,15 @@ export const handleStageZoom = ( event: KonvaEventObject, options: AnnotationViewerOptions, ) => { - if (!stage || !imageBoundingBox) return + if (!stage || !imageBoundingBox || !options.zoom) return 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 @@ -117,11 +128,11 @@ export const handleStageZoom = ( stage.draggable(false) stage.scale({ x: imageBoundingBox.scale, y: imageBoundingBox.scale }) stage.position({ x: imageBoundingBox.x, y: imageBoundingBox.y }) - stage.setAttr('zoomScale', 1) + setZoomScale(stage, 1) } else { stage.draggable(true) stage.scale({ x: newScale, y: newScale }) - stage.setAttr('zoomScale', newScale / imageBoundingBox.scale) + setZoomScale(stage, newScale / imageBoundingBox.scale) stage.position(newPos) } diff --git a/tsconfig.json b/tsconfig.json index b2dfa29..cb7e200 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,22 @@ { "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"], "strict": true, + "isolatedModules": 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..7e3c08c --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,32 @@ +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: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + plugins: [ + react(), + dts({ + tsconfigPath: './tsconfig.json', + include: ['src'], + exclude: ['src/**/*.spec.*'], + bundleTypes: true, + }), + ], + build: { + lib: { + entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)), + formats: ['es'], + fileName: 'index', + }, + rollupOptions: { + external: ['react', 'react-dom', 'react/jsx-runtime'], + }, + }, +}) From 0d67d2438bb73ced98feade9d529284e44cab326 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:14:57 +0200 Subject: [PATCH 02/10] chg: :recycle: fix vulnerabilities --- pnpm-lock.yaml | 604 +++++++++++--------------------------------- pnpm-workspace.yaml | 4 + 2 files changed, 145 insertions(+), 463 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09ab000..38ac078 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + pdfjs-dist@<=4.1.392: ^4.2.67 + tar@<=7.5.20: ^7.5.21 + uuid@<11.1.1: ^11.1.1 + importers: .: @@ -173,14 +178,14 @@ importers: specifier: ^8.1.3 version: 8.4.3 pdfjs-dist: - specifier: 3.11.174 - version: 3.11.174(supports-color@8.1.1) + specifier: ^4.2.67 + version: 4.10.38 utif: specifier: ^3.1.0 version: 3.1.0 uuid: - specifier: ^8.3.2 - version: 8.3.2 + specifier: ^11.1.1 + version: 11.1.1 devDependencies: '@eslint/js': specifier: ^10.0.1 @@ -439,10 +444,6 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@mapbox/node-pre-gyp@1.0.11': - resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true - '@microsoft/api-extractor-model@7.33.14': resolution: {integrity: sha512-HdFzU14NGrm04SxA8CfEByX4R1S06o08qVCrGgIepb4miUgky771TwiDi2b3/ZJSQ1QKEJzq452MW09zenL19w==} engines: {node: '>=20.9.0'} @@ -458,6 +459,81 @@ packages: '@microsoft/tsdoc@0.17.0': resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==} + '@napi-rs/canvas-android-arm64@0.1.100': + resolution: {integrity: sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/canvas-darwin-arm64@0.1.100': + resolution: {integrity: sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/canvas-darwin-x64@0.1.100': + resolution: {integrity: sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + resolution: {integrity: sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + resolution: {integrity: sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-arm64-musl@0.1.100': + resolution: {integrity: sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + resolution: {integrity: sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-gnu@0.1.100': + resolution: {integrity: sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-musl@0.1.100': + resolution: {integrity: sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + resolution: {integrity: sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@0.1.100': + resolution: {integrity: sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/canvas@0.1.100': + resolution: {integrity: sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==} + engines: {node: '>= 10'} + '@oxc-project/types@0.151.0': resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} @@ -619,9 +695,6 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@20.19.43': - resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} - '@types/node@24.19.0': resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==} @@ -740,9 +813,6 @@ packages: typescript: optional: true - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -753,10 +823,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -797,17 +863,9 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - aproba@2.1.0: - resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} - arch@3.0.0: resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} - 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. - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -831,9 +889,6 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - 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} @@ -855,9 +910,6 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - brace-expansion@1.1.21: - resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - brace-expansion@5.0.12: resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} @@ -888,10 +940,6 @@ packages: 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'} - caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -899,10 +947,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - chrome-remote-interface@0.33.3: resolution: {integrity: sha512-zNnn0prUL86Teru6UCAZ1yU1XeXljHl3gj7OrfPcarEfU62OUU4IujDPdTDW3dAWwRqN3ZMG/Chhkh2gPL/wiw==} hasBin: true @@ -930,10 +974,6 @@ packages: 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==} @@ -959,18 +999,12 @@ packages: 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==} confbox@0.3.1: resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} - console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1013,10 +1047,6 @@ packages: supports-color: optional: true - 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==} @@ -1024,9 +1054,6 @@ packages: 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'} @@ -1211,13 +1238,6 @@ packages: 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==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1226,11 +1246,6 @@ 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'} @@ -1265,10 +1280,6 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - 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 - global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} @@ -1302,9 +1313,6 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -1336,10 +1344,6 @@ packages: 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'} - human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -1363,13 +1367,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - 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@2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} @@ -1591,10 +1588,6 @@ packages: 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'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -1618,10 +1611,6 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - mimic-response@2.1.0: - resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} - engines: {node: '>=8'} - minimatch@10.2.3: resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} engines: {node: 18 || 20 || >=22} @@ -1630,38 +1619,15 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - 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==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1670,36 +1636,14 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - 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-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'} - hasBin: true - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - deprecated: This package is no longer supported. - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -1740,10 +1684,6 @@ packages: 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@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1751,16 +1691,12 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - 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@4.10.38: + resolution: {integrity: sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==} + engines: {node: '>=20'} pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -1848,10 +1784,6 @@ packages: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -1871,11 +1803,6 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rolldown@1.2.11: resolution: {integrity: sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1909,9 +1836,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1943,12 +1867,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@3.1.1: - resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} - slice-ansi@7.1.2: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} @@ -1992,9 +1910,6 @@ packages: resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} engines: {node: '>=20'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2025,11 +1940,6 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true - 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 - throttleit@1.0.1: resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} @@ -2052,9 +1962,6 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -2110,9 +2017,6 @@ packages: ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -2170,12 +2074,8 @@ packages: 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). + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true verror@1.10.0: @@ -2246,23 +2146,14 @@ packages: vscode-uri@3.2.0: resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - 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==} - word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2289,9 +2180,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yauzl@3.4.0: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} engines: {node: '>=12'} @@ -2538,22 +2426,6 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@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 - optional: true - '@microsoft/api-extractor-model@7.33.14(@types/node@24.19.0)': dependencies: '@microsoft/tsdoc': 0.17.0 @@ -2589,6 +2461,54 @@ snapshots: '@microsoft/tsdoc@0.17.0': {} + '@napi-rs/canvas-android-arm64@0.1.100': + optional: true + + '@napi-rs/canvas-darwin-arm64@0.1.100': + optional: true + + '@napi-rs/canvas-darwin-x64@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-arm64-musl@0.1.100': + optional: true + + '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-x64-gnu@0.1.100': + optional: true + + '@napi-rs/canvas-linux-x64-musl@0.1.100': + optional: true + + '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + optional: true + + '@napi-rs/canvas-win32-x64-msvc@0.1.100': + optional: true + + '@napi-rs/canvas@0.1.100': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 0.1.100 + '@napi-rs/canvas-darwin-arm64': 0.1.100 + '@napi-rs/canvas-darwin-x64': 0.1.100 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.100 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.100 + '@napi-rs/canvas-linux-arm64-musl': 0.1.100 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.100 + '@napi-rs/canvas-linux-x64-gnu': 0.1.100 + '@napi-rs/canvas-linux-x64-musl': 0.1.100 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.100 + '@napi-rs/canvas-win32-x64-msvc': 0.1.100 + optional: true + '@oxc-project/types@0.151.0': {} '@rolldown/binding-android-arm-eabi@1.2.11': @@ -2703,17 +2623,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@20.19.43': - dependencies: - undici-types: 6.21.0 - '@types/node@24.19.0': dependencies: undici-types: 7.24.6 '@types/pixelmatch@5.2.6': dependencies: - '@types/node': 20.19.43 + '@types/node': 24.19.0 '@types/react-dom@19.3.0(@types/react@19.3.0)': dependencies: @@ -2845,22 +2761,12 @@ snapshots: optionalDependencies: typescript: 6.0.3 - abbrev@1.1.1: - optional: true - acorn-jsx@5.3.2(acorn@8.18.0): dependencies: acorn: 8.18.0 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 - optional: true - ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -2904,17 +2810,8 @@ snapshots: ansi-styles@6.2.3: {} - aproba@2.1.0: - optional: true - arch@3.0.0: {} - are-we-there-yet@2.0.0: - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - optional: true - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -2933,9 +2830,6 @@ snapshots: aws4@1.13.2: {} - balanced-match@1.0.2: - optional: true - balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -2950,12 +2844,6 @@ snapshots: bluebird@3.7.2: {} - brace-expansion@1.1.21: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - optional: true - brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -2995,16 +2883,6 @@ snapshots: 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 - caseless@0.12.0: {} chalk@4.1.2: @@ -3012,9 +2890,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chownr@2.0.0: - optional: true - chrome-remote-interface@0.33.3: dependencies: commander: 2.11.0 @@ -3046,9 +2921,6 @@ snapshots: color-name@1.1.4: {} - color-support@1.1.3: - optional: true - colorette@2.0.20: {} colors@1.4.0: @@ -3066,16 +2938,10 @@ snapshots: compare-versions@6.1.1: {} - concat-map@0.0.1: - optional: true - confbox@0.1.8: {} confbox@0.3.1: {} - console-control-strings@1.1.0: - optional: true - convert-source-map@2.0.0: {} core-util-is@1.0.2: {} @@ -3151,18 +3017,10 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - decompress-response@4.2.1: - dependencies: - mimic-response: 2.1.0 - optional: true - deep-is@0.1.4: {} delayed-stream@1.0.0: {} - delegates@1.0.0: - optional: true - detect-libc@2.1.2: {} diff@8.0.4: {} @@ -3370,32 +3228,11 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - optional: true - - fs.realpath@1.0.0: - optional: true - 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-east-asian-width@1.7.0: {} @@ -3434,16 +3271,6 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@7.2.3: - 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 - optional: true - global-dirs@3.0.1: dependencies: ini: 2.0.0 @@ -3466,9 +3293,6 @@ snapshots: dependencies: has-symbols: 1.1.0 - has-unicode@2.0.1: - optional: true - hasha@5.2.2: dependencies: is-stream: 2.0.1 @@ -3500,14 +3324,6 @@ snapshots: jsprim: 2.0.2 sshpk: 1.18.0 - https-proxy-agent@5.0.1(supports-color@8.1.1): - dependencies: - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - optional: true - human-signals@1.1.1: {} ieee754@1.2.1: {} @@ -3520,15 +3336,6 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - optional: true - - inherits@2.0.4: - optional: true - ini@2.0.0: {} is-core-module@2.17.0: @@ -3715,11 +3522,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 - make-dir@3.1.0: - dependencies: - semver: 6.3.1 - optional: true - math-intrinsics@1.1.0: {} merge-stream@2.0.0: {} @@ -3734,9 +3536,6 @@ snapshots: mimic-function@5.0.1: {} - mimic-response@2.1.0: - optional: true - minimatch@10.2.3: dependencies: brace-expansion: 5.0.12 @@ -3745,30 +3544,8 @@ snapshots: dependencies: brace-expansion: 5.0.12 - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.21 - optional: true - minimist@1.2.8: {} - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - optional: true - - minipass@5.0.0: - optional: true - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - optional: true - - mkdirp@1.0.4: - optional: true - mlly@1.8.2: dependencies: acorn: 8.18.0 @@ -3778,40 +3555,16 @@ snapshots: ms@2.1.3: {} - nan@2.29.0: - optional: true - nanoid@3.3.19: {} natural-compare@1.4.0: {} - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - optional: true - node-releases@2.0.57: {} - nopt@5.0.0: - dependencies: - abbrev: 1.1.1 - optional: true - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - npmlog@5.0.1: - 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 - - object-assign@4.1.1: - optional: true - object-inspect@1.13.4: {} once@1.4.0: @@ -3851,25 +3604,15 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: - optional: true - path-key@3.1.1: {} path-parse@1.0.7: {} - path2d-polyfill@2.0.1: - optional: true - pathe@2.0.3: {} - pdfjs-dist@3.11.174(supports-color@8.1.1): + pdfjs-dist@4.10.38: optionalDependencies: - canvas: 2.11.2(supports-color@8.1.1) - path2d-polyfill: 2.0.1 - transitivePeerDependencies: - - encoding - - supports-color + '@napi-rs/canvas': 0.1.100 pend@1.2.0: {} @@ -3942,13 +3685,6 @@ snapshots: react@19.3.0: {} - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - optional: true - request-progress@3.0.0: dependencies: throttleit: 1.0.1 @@ -3969,11 +3705,6 @@ snapshots: rfdc@1.4.1: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - optional: true - rolldown@1.2.11: dependencies: '@oxc-project/types': 0.151.0 @@ -4012,9 +3743,6 @@ snapshots: semver@7.8.5: {} - set-blocking@2.0.0: - optional: true - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4053,16 +3781,6 @@ snapshots: signal-exit@4.1.0: {} - 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 - slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -4112,11 +3830,6 @@ snapshots: get-east-asian-width: 1.7.0 strip-ansi: 7.2.0 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -4139,16 +3852,6 @@ snapshots: systeminformation@5.33.14: {} - 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 - throttleit@1.0.1: {} tinyglobby@0.2.17: @@ -4168,9 +3871,6 @@ snapshots: dependencies: tldts: 6.1.86 - tr46@0.0.3: - optional: true - tree-kill@1.2.2: {} ts-api-utils@2.5.0(typescript@6.0.3): @@ -4210,8 +3910,6 @@ snapshots: ufo@1.6.4: {} - undici-types@6.21.0: {} - undici-types@7.24.6: {} universalify@2.0.1: {} @@ -4259,10 +3957,7 @@ snapshots: dependencies: pako: 1.0.11 - util-deprecate@1.0.2: - optional: true - - uuid@8.3.2: {} + uuid@11.1.1: {} verror@1.10.0: dependencies: @@ -4310,26 +4005,12 @@ snapshots: vscode-uri@3.2.0: {} - webidl-conversions@3.0.1: - optional: true - webpack-virtual-modules@0.6.2: {} - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - optional: true - which@2.0.2: dependencies: isexe: 2.0.0 - wide-align@1.1.5: - dependencies: - string-width: 4.2.3 - optional: true - word-wrap@1.2.5: {} wrap-ansi@9.0.2: @@ -4344,9 +4025,6 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: - optional: true - yauzl@3.4.0: dependencies: pend: 1.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e423dbd..3399aed 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,7 @@ allowBuilds: canvas: false cypress: true esbuild: true +overrides: + pdfjs-dist@<=4.1.392: ^4.2.67 + tar@<=7.5.20: ^7.5.21 + uuid@<11.1.1: ^11.1.1 From 9f6f8cb8c90abf080b65ec1a232f8fcfe0f5a605 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:23:05 +0200 Subject: [PATCH 03/10] chore: :wrench: add more aggressive typing and linting rules (#39) --- .prettierrc | 2 +- README.md | 12 +- cypress.config.ts | 15 +- cypress/assets/shapes.ts | 4 +- cypress/support/component.ts | 8 +- cypress/support/index.d.ts | 4 +- eslint.config.ts | 98 +++++++- package.json | 5 +- pnpm-lock.yaml | 38 --- src/common/constants.ts | 46 ++-- src/common/types.ts | 152 ++++++------ src/components/AnnotationLens.spec.tsx | 142 +++++------ src/components/AnnotationLens.tsx | 164 +++++++------ src/components/AnnotationViewer.spec.tsx | 293 +++++++++++++---------- src/components/AnnotationViewer.tsx | 291 ++++++++++++---------- src/components/dynamicZoom.spec.tsx | 71 +++--- src/declarations.d.ts | 16 +- src/index.ts | 22 +- src/utils/canvas.spec.ts | 48 ++-- src/utils/canvas.ts | 128 +++++----- src/utils/functions.ts | 62 ++--- src/utils/getImagesFromPDF.spec.tsx | 26 +- src/utils/getImagesFromPDF.ts | 62 ++--- src/utils/getPDFPageCount.spec.tsx | 20 +- src/utils/getPDFPageCount.ts | 10 +- src/utils/image.spec.ts | 38 +-- src/utils/image.ts | 218 +++++++++-------- src/utils/layer.ts | 12 +- src/utils/orientation.ts | 126 +++++----- src/utils/roundTo.spec.ts | 40 ++-- src/utils/roundTo.ts | 10 +- src/utils/selection.spec.ts | 28 +-- src/utils/selection.ts | 135 ++++++----- src/utils/useEventListener.ts | 26 +- src/utils/useMultiSelection.ts | 36 +-- src/utils/zoom.spec.ts | 58 ++--- src/utils/zoom.ts | 160 +++++++------ tsconfig.json | 7 +- vite.config.ts | 14 +- 39 files changed, 1432 insertions(+), 1215 deletions(-) diff --git a/.prettierrc b/.prettierrc index f058170..d1a7462 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,7 @@ "tabWidth": 2, "singleQuote": true, "trailingComma": "all", - "semi": false, + "semi": true, "importOrder": [ "", "^(react/(.*)$)|^(react$)", diff --git a/README.md b/README.md index 0dca3a7..3376ce3 100644 --- a/README.md +++ b/README.md @@ -44,9 +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 dummyImage from 'path-to-your/file.jpg' -import { AnnotationViewer } from 'react-mindee-js' +import React from 'react'; +import dummyImage from 'path-to-your/file.jpg'; +import { AnnotationViewer } from 'react-mindee-js'; const dummyShapes = [ { @@ -67,15 +67,15 @@ const dummyShapes = [ [0.394, 0.087], ], }, -] +]; const data = { image: dummyImage, shapes: dummyShapes, -} +}; function App() { - return + return ; } ``` diff --git a/cypress.config.ts b/cypress.config.ts index 5a2755d..98ceb98 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,7 +1,6 @@ -import { addMatchImageSnapshotPlugin } from '@simonsmith/cypress-image-snapshot/plugin' -import { defineConfig } from 'cypress' -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,9 +16,9 @@ export default defineConfig({ // if (config.testingType === 'component') // on('dev-server:start', async (options) => startDevServer({ options })) - addMatchImageSnapshotPlugin(on) + addMatchImageSnapshotPlugin(on); - return config + return config; }, specPattern: 'src/**/*.spec.{js,ts,jsx,tsx}', devServer: { @@ -27,8 +26,8 @@ export default defineConfig({ bundler: 'vite', viteConfig: defineViteConfig({ assetsInclude: ['**/*.tiff', '**/*.heic'], - plugins: [tsconfigPaths()], + resolve: { tsconfigPaths: true }, }), }, }, -}) +}); diff --git a/cypress/assets/shapes.ts b/cypress/assets/shapes.ts index 17e24cf..771873f 100644 --- a/cypress/assets/shapes.ts +++ b/cypress/assets/shapes.ts @@ -1,4 +1,4 @@ -import type { 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/support/component.ts b/cypress/support/component.ts index 437ee8f..4a3515d 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -1,9 +1,9 @@ -import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command' -import { mount } from 'cypress/react' +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 43a27b3..ed8caa9 100644 --- a/cypress/support/index.d.ts +++ b/cypress/support/index.d.ts @@ -1,9 +1,9 @@ -import type { mount } from 'cypress/react' +import type { mount } from 'cypress/react'; declare global { namespace Cypress { interface Chainable { - mount: typeof mount + mount: typeof mount; } } } diff --git a/eslint.config.ts b/eslint.config.ts index 8d2fc8f..6c97a3b 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,9 +1,9 @@ -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' +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'] }, @@ -12,6 +12,9 @@ export default defineConfig( tseslint.configs.stylisticTypeChecked, reactHooks.configs.flat['recommended-latest'], { + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, languageOptions: { globals: { ...globals.browser, ...globals.es2022 }, parserOptions: { @@ -20,19 +23,86 @@ export default defineConfig( }, }, 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/consistent-type-imports': 'error', - '@typescript-eslint/no-floating-promises': 'error', + '@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'] }], }, }, @@ -45,11 +115,21 @@ export default defineConfig( '@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 5b221f9..3b58c11 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "typescript": "^6.0.3", "typescript-eslint": "^8.71.0", "vite": "^8.3.1", - "vite-plugin-dts": "^5.1.1", - "vite-tsconfig-paths": "^6.1.1" + "vite-plugin-dts": "^5.1.1" }, "peerDependencies": { "react": "^18 || ^19", @@ -82,7 +81,7 @@ "docs:deploy": "pnpm --dir ./docs deploy", "prettier:check": "prettier --check .", "prettier:write": "prettier --write .", - "lint": "tsc --noEmit && eslint . && prettier --check .", + "lint": "tsc --noEmit && eslint --max-warnings 0 . && prettier --check .", "lint:fix": "eslint --fix . && prettier --write ." }, "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38ac078..e5b8602 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,9 +256,6 @@ importers: vite-plugin-dts: 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.1(@types/node@24.19.0)(jiti@2.7.0)) - vite-tsconfig-paths: - specifier: ^6.1.1 - version: 6.1.1(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0)) packages: @@ -1288,9 +1285,6 @@ packages: resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} engines: {node: '>=18'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - glur@1.1.2: resolution: {integrity: sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==} @@ -1972,17 +1966,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - deprecated: unmaintained - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -2095,11 +2078,6 @@ packages: vite: optional: true - vite-tsconfig-paths@6.1.1: - resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} - peerDependencies: - vite: '*' - vite@8.3.1: resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3277,8 +3255,6 @@ snapshots: globals@17.12.0: {} - globrex@0.1.2: {} - glur@1.1.2: {} gopd@1.2.0: {} @@ -3877,10 +3853,6 @@ snapshots: dependencies: typescript: 6.0.3 - tsconfck@3.1.6(typescript@6.0.3): - optionalDependencies: - typescript: 6.0.3 - tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -3981,16 +3953,6 @@ snapshots: - typescript - webpack - vite-tsconfig-paths@6.1.1(supports-color@8.1.1)(typescript@6.0.3)(vite@8.3.1(@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@6.0.3) - vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) - transitivePeerDependencies: - - supports-color - - typescript - vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0): dependencies: lightningcss: 1.33.0 diff --git a/src/common/constants.ts b/src/common/constants.ts index 1b9170a..f938924 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,45 +1,45 @@ -import type { Line } from 'konva/lib/shapes/Line' +import type { Line } from 'konva/lib/shapes/Line'; -import type { AnnotationData } from '@/common/types' +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.stroke() - polygon.setAttr('fill', `${stroke}40`) - polygon.draw() + onMouseEnter: (polygon: Line): void => { + const stroke = polygon.stroke(); + polygon.setAttr('fill', `${stroke}40`); + polygon.draw(); }, shapeConfig: { stroke: STROKE_COLOR, @@ -51,7 +51,7 @@ export const DEFAULT_ANNOTATION_VIEWER_OPTIONS = { max: 10, defaultZoom: 1, }, -} +}; export const DEFAULT_ANNOTATION_LENS_OPTIONS = { shapeConfig: { @@ -59,4 +59,4 @@ export const DEFAULT_ANNOTATION_LENS_OPTIONS = { strokeWidth: 1, listening: false, }, -} +}; diff --git a/src/common/types.ts b/src/common/types.ts index ccb2296..0989944 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,91 +1,91 @@ -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 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'; export type AnnotationShape = T & { - id: string - coordinates: number[][] - config?: LineConfig -} + id: string; + coordinates: number[][]; + config?: LineConfig; +}; -export type Orientation = 0 | 90 | 180 | 270 +export type Orientation = 0 | 90 | 180 | 270; -export interface BaseOptions { - shapeConfig?: LineConfig -} +export type BaseOptions = { + shapeConfig?: LineConfig; +}; -export interface AnnotationLayers { - shapes: Konva.Layer - image: Konva.Layer -} +export type AnnotationLayers = { + 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; zoom?: { - modifier: number - max: number - defaultZoom: number - } -} + modifier: number; + max: number; + defaultZoom: number; + }; +}; -export interface ImageBoundingBox { - x: number - y: number - width: number - height: number - scale: number -} +export type ImageBoundingBox = { + x: number; + y: number; + width: number; + height: number; + scale: number; +}; -export interface PointerPosition { - x: number - y: number -} +export type PointerPosition = { + x: number; + y: number; +}; -export interface AnnotationData { - image?: string | null - shapes?: AnnotationShape[] - orientation?: Orientation -} +export type AnnotationData = { + image?: string | null; + shapes?: AnnotationShape[]; + orientation?: Orientation; +}; -export interface ImageData { - element: HTMLImageElement - shape: Konva.Image -} +export type ImageData = { + element: HTMLImageElement; + shape: Konva.Image; +}; -export interface 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 -} +export type ZoomOptions = { + 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 37d6eaa..3c12d30 100644 --- a/src/components/AnnotationLens.spec.tsx +++ b/src/components/AnnotationLens.spec.tsx @@ -1,34 +1,34 @@ -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 dummyImage from 'cypress/assets/demo.jpg'; +import { dummyShapes } from 'cypress/assets/shapes'; -import type { 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; -interface TesterProps { - containerWidth: number - containerHeight: number - id?: string -} +type TesterProps = { + 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 +52,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', - ) - }) - }) -}) + ); + }); + }); +}); diff --git a/src/components/AnnotationLens.tsx b/src/components/AnnotationLens.tsx index f6c855b..3329799 100644 --- a/src/components/AnnotationLens.tsx +++ b/src/components/AnnotationLens.tsx @@ -1,5 +1,11 @@ -import React, { useEffect, useId, useRef, useState } from 'react' -import Konva from 'konva' +import React, { + useEffect, + useId, + useRef, + useState, + type ReactElement, +} from 'react'; +import Konva from 'konva'; import { DEFAULT_ANNOTATION_LENS_OPTIONS, @@ -7,33 +13,35 @@ import { DEFAULT_LENS_ZOOM_LEVEL, DEFAULT_POINTER_POSITION, DEFAULT_STYLE, - KONVA_REFS, -} from '@/common/constants' + KonvaRefs, +} from '@/common/constants'; import type { AnnotationLensOptions, AnnotationLensProps, AnnotationShape, ImageBoundingBox, ImageData, -} from '@/common/types' +} from '@/common/types'; -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' +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, -) => { - shapesLayer.destroyChildren() - if (!shapes) return - mapShapesToPolygons(shapesLayer, shapes, false, imageBoundingBox, options) - shapesLayer.batchDraw() -} +): void => { + shapesLayer.destroyChildren(); + if (!shapes) { + return; + } + mapShapesToPolygons(shapesLayer, shapes, false, imageBoundingBox, options); + shapesLayer.batchDraw(); +}; export default function AnnotationLens({ id, @@ -43,88 +51,96 @@ export default function AnnotationLens({ style = {}, options: customOptions = {}, data = DEFAULT_DATA, -}: AnnotationLensProps) { - const generatedId = useId() - const containerId = id ?? generatedId +}: AnnotationLensProps): ReactElement { + const generatedId = useId(); + const containerId = id ?? generatedId; const options: AnnotationLensOptions = { ...DEFAULT_ANNOTATION_LENS_OPTIONS, ...customOptions, - } - const optionsKey = JSON.stringify(options) - const optionsRef = useRef(options) + }; + const optionsKey = JSON.stringify(options); + const optionsRef = useRef(options); useEffect(() => { - optionsRef.current = options - }) + 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) - const [imageVersion, setImageVersion] = useState(0) + }); + const stageObject = useRef(null); + const imageBoundingBoxObject = useRef(null); + const [imageVersion, setImageVersion] = useState(0); - const getStageRef = useRef(getStage) + const getStageRef = useRef(getStage); useEffect(() => { - getStageRef.current = getStage - }) + getStageRef.current = getStage; + }); useEffect(() => { - const container = containerRef.current - if (!container) return - 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 container = containerRef.current; + if (!container) { + return; } - }, []) + 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 { image, orientation, shapes } = data + const { image, orientation, shapes } = data; useEffect(() => { - if (!image) { - clearLayers(layersObject.current) - imageDataObject.current.element = new Image() - imageDataObject.current.shape.image(imageDataObject.current.element) - imageBoundingBoxObject.current = null - return + 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; } - const controller = new AbortController() - const { signal } = controller + const controller = new AbortController(); + const { signal } = controller; void (async () => { try { - const src = orientation ? await rotateImage(image, orientation) : image - if (signal.aborted) return - const element = new Image() + const src = await rotateImage(image, orientation); + if (signal.aborted) { + return; + } + const element = new Image(); element.onload = () => { - if (signal.aborted) return - imageDataObject.current.element = element - imageDataObject.current.shape.image(element) + 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 + ) ?? null; + setImageVersion((v) => v + 1); + }; + element.src = src; } catch (error) { - if (!signal.aborted) console.error(error) + if (!signal.aborted) { + console.error(error); + } } - })() + })(); return () => { - controller.abort() - } - }, [image, orientation]) + controller.abort(); + }; + }, [image, orientation]); useEffect(() => { handleLensZoom( @@ -132,8 +148,8 @@ export default function AnnotationLens({ imageBoundingBoxObject.current, pointerPosition, zoomLevel, - ) - }, [pointerPosition, zoomLevel, imageVersion]) + ); + }, [pointerPosition, zoomLevel, imageVersion]); useEffect(() => { drawLensShapes( @@ -141,8 +157,8 @@ export default function AnnotationLens({ shapes, imageBoundingBoxObject.current, optionsRef.current, - ) - }, [shapes, optionsKey, imageVersion]) + ); + }, [shapes, optionsKey, imageVersion]); return (
- ) + ); } diff --git a/src/components/AnnotationViewer.spec.tsx b/src/components/AnnotationViewer.spec.tsx index d23a5e3..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 { 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 Konva from 'konva'; -import type { AnnotationData } from '@/common/types' +import { KonvaRefs } from '@/common/constants'; +import type { AnnotationData } from '@/common/types'; -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', () => { @@ -88,92 +90,92 @@ describe('AnnotationViewer', () => { , - ) - 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, @@ -183,47 +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 onShapeClick = cy.spy().as('onShapeClick') - const onShapeMouseEnter = cy.spy().as('onShapeMouseEnter') - const onShapeMouseLeave = cy.spy().as('onShapeMouseLeave') + const onShapeClick = cy.spy().as('onShapeClick'); + const onShapeMouseEnter = cy.spy().as('onShapeMouseEnter'); + const onShapeMouseLeave = cy.spy().as('onShapeMouseLeave'); cy.mount( , - ) - cy.wait(1000) + ); + cy.wait(1000); - cy.get(`#${containerId} .konvajs-content`).trigger('mousemove', 350, 50) + 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}`).click(350, 50); + cy.get('@onShapeClick').should('have.been.calledOnceWith', dummyShapes[1]); - cy.get(`#${containerId} .konvajs-content`).trigger('mousemove', 10, 10) + 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') - }) + ); + 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(() => { @@ -244,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 } @@ -294,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 984c40d..7892f7f 100644 --- a/src/components/AnnotationViewer.tsx +++ b/src/components/AnnotationViewer.tsx @@ -1,36 +1,42 @@ -import React, { useEffect, useId, useRef, useState } from 'react' -import Konva from 'konva' +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' + 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 { getZoomScale, handleStageZoom, handleZoomScale } from '@/utils/zoom' +} from '@/utils/selection'; +import useMultiSelection from '@/utils/useMultiSelection'; +import { getZoomScale, handleStageZoom, handleZoomScale } from '@/utils/zoom'; type ShapeCallbacks = Pick< AnnotationViewerProps, @@ -40,7 +46,7 @@ type ShapeCallbacks = Pick< | 'onShapeMouseEnter' | 'onShapeMouseLeave' | 'onShapeMultiSelect' -> +>; const drawViewerShapes = ( shapesLayer: Konva.Layer, @@ -49,13 +55,15 @@ const drawViewerShapes = ( options: AnnotationViewerOptions, selectionRect: Konva.Rect, callbacks: ShapeCallbacks, -) => { - shapesLayer.destroyChildren() - if (options.enableSelection) { - selectionRect.setAttrs({ ...options.selectionRectConfig }) - shapesLayer.add(selectionRect) +): void => { + shapesLayer.destroyChildren(); + if (options.enableSelection === true) { + selectionRect.setAttrs({ ...options.selectionRectConfig }); + shapesLayer.add(selectionRect); + } + if (!shapes) { + return; } - if (!shapes) return mapShapesToPolygons( shapesLayer, shapes, @@ -65,9 +73,9 @@ const drawViewerShapes = ( callbacks.onShapeClick, callbacks.onShapeMouseEnter, callbacks.onShapeMouseLeave, - ) - shapesLayer.batchDraw() -} + ); + shapesLayer.batchDraw(); +}; export default function AnnotationViewer({ id, @@ -82,33 +90,33 @@ export default function AnnotationViewer({ onShapeClick, options: customOptions = {}, data = DEFAULT_DATA, -}: AnnotationViewerProps) { - const generatedId = useId() - const containerId = id ?? generatedId +}: AnnotationViewerProps): ReactElement { + const generatedId = useId(); + const containerId = id ?? generatedId; const options: AnnotationViewerOptions = { ...DEFAULT_ANNOTATION_VIEWER_OPTIONS, ...customOptions, - } - const optionsKey = JSON.stringify(options) - 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) - const [imageVersion, setImageVersion] = useState(0) + }); + const stageObject = useRef(null); + const imageBoundingBoxObject = useRef(null); + const [imageVersion, setImageVersion] = useState(0); - const optionsRef = useRef(options) - const callbacksRef = useRef({}) + const optionsRef = useRef(options); + const callbacksRef = useRef({}); useEffect(() => { - optionsRef.current = options + optionsRef.current = options; callbacksRef.current = { getPointerPosition, getStage, @@ -116,32 +124,36 @@ export default function AnnotationViewer({ onShapeMouseEnter, onShapeMouseLeave, onShapeMultiSelect, - } - }) + }; + }); - useMultiSelection({ stageRef: stageObject, isSelectionActiveRef }) + useMultiSelection({ stageRef: stageObject, isSelectionActiveRef }); - const fitImageToContainer = () => { + const fitImageToContainer = (): void => { const imageBoundingBox = handleResizeImage( stageObject.current, containerRef.current, imageDataObject.current, - ) - if (!imageBoundingBox) return - imageBoundingBoxObject.current = imageBoundingBox - layersObject.current.image.batchDraw() - setImageVersion((v) => v + 1) - } + ); + if (!imageBoundingBox) { + return; + } + imageBoundingBoxObject.current = imageBoundingBox; + layersObject.current.image.batchDraw(); + setImageVersion((v) => v + 1); + }; useEffect(() => { - const container = containerRef.current - if (!container) 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) + const container = containerRef.current; + if (!container) { + 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); stage.on('wheel', (event) => { handleStageZoom( @@ -149,96 +161,113 @@ export default function AnnotationViewer({ imageBoundingBoxObject.current, event, optionsRef.current, - ) - }) + ); + }); stage.on('mousemove', () => { - const { getPointerPosition } = callbacksRef.current - if (!getPointerPosition) return + const { getPointerPosition: getPointerPosCallback } = + callbacksRef.current; + if (!getPointerPosCallback) { + return; + } const mousePointTo = getMousePosition( stage, imageBoundingBoxObject.current, - ) - if (mousePointTo) getPointerPosition(mousePointTo) - }) + ); + if (mousePointTo) { + getPointerPosCallback(mousePointTo); + } + }); stage.on('mousedown touchstart', (event) => { - if (!optionsRef.current.enableSelection) return + if (optionsRef.current.enableSelection !== true) { + return; + } onSelectionStart( event, layers.shapes, selectionRectObject.current, isSelectionActiveRef.current, - ) - }) + ); + }); stage.on('mousemove touchmove', () => { - if (!optionsRef.current.enableSelection) return - onSelectionMove(layers.shapes, selectionRectObject.current) - }) + if (optionsRef.current.enableSelection !== true) { + return; + } + onSelectionMove(layers.shapes, selectionRectObject.current); + }); stage.on('mouseup touchend', () => { - if (!optionsRef.current.enableSelection) return + if (optionsRef.current.enableSelection !== true) { + return; + } onSelectionEnd( layers.shapes, selectionRectObject.current, callbacksRef.current.onShapeMultiSelect, - ) - }) + ); + }); stage.on('dragstart', () => { - if (!optionsRef.current.enableSelection) return - stage.container().style.cursor = 'grabbing' - }) + if (optionsRef.current.enableSelection !== true) { + return; + } + stage.container().style.cursor = 'grabbing'; + }); stage.on('dragend', () => { - if (!optionsRef.current.enableSelection) return - stage.container().style.cursor = 'pointer' - }) + if (optionsRef.current.enableSelection !== true) { + return; + } + stage.container().style.cursor = 'pointer'; + }); return () => { - stage.destroy() - stageObject.current = null - } - }, []) + stage.destroy(); + stageObject.current = null; + }; + }, []); useEffect(() => { - window.addEventListener('resize', fitImageToContainer) + window.addEventListener('resize', fitImageToContainer); return () => { - window.removeEventListener('resize', fitImageToContainer) - } - }, []) + window.removeEventListener('resize', fitImageToContainer); + }; + }, []); - const { image, orientation, shapes } = data + const { image, orientation, shapes } = data; useEffect(() => { - if (!image) { - clearLayers(layersObject.current) - imageDataObject.current.element = new Image() - imageDataObject.current.shape.image(imageDataObject.current.element) - imageBoundingBoxObject.current = null - return + 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; } - const controller = new AbortController() - const { signal } = controller + const controller = new AbortController(); + const { signal } = controller; void (async () => { try { - const prepared = await prepareImage(image) - signal.throwIfAborted() - const src = orientation - ? await rotateImage(prepared, orientation) - : prepared - signal.throwIfAborted() - const element = new 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 + if (signal.aborted) { + return; + } + imageDataObject.current.element = element; + imageDataObject.current.shape.image(element); + fitImageToContainer(); + }; + element.src = src; } catch (error) { - if (!signal.aborted) console.error(error) + if (!signal.aborted) { + console.error(error); + } } - })() - return () => { - controller.abort() - } - }, [image, orientation]) + })(); + return (): void => { + controller.abort(); + }; + }, [image, orientation]); useEffect(() => { drawViewerShapes( @@ -249,10 +278,10 @@ export default function AnnotationViewer({ ...optionsRef.current, onClick: (polygon) => optionsRef.current.onClick?.(polygon), onMouseEnter: (polygon) => { - optionsRef.current.onMouseEnter?.(polygon) + optionsRef.current.onMouseEnter?.(polygon); }, onMouseLeave: (polygon) => { - optionsRef.current.onMouseLeave?.(polygon) + optionsRef.current.onMouseLeave?.(polygon); }, }, selectionRectObject.current, @@ -263,23 +292,27 @@ export default function AnnotationViewer({ onShapeMouseLeave: (shape) => callbacksRef.current.onShapeMouseLeave?.(shape), }, - ) - }, [shapes, optionsKey, imageVersion]) + ); + }, [shapes, optionsKey, imageVersion]); useEffect(() => { - if (!customZoomLevel) return + if (customZoomLevel === undefined) { + return; + } handleZoomScale( stageObject.current, customZoomLevel, imageBoundingBoxObject.current, - ) - }, [customZoomLevel, imageVersion]) + ); + }, [customZoomLevel, imageVersion]); useEffect(() => { - const stage = stageObject.current - const imageBoundingBox = imageBoundingBoxObject.current - if (!customStagePosition || !stage || !imageBoundingBox) return - const zoomScale = getZoomScale(stage) + const stage = stageObject.current; + const imageBoundingBox = imageBoundingBoxObject.current; + if (!customStagePosition || !stage || !imageBoundingBox) { + return; + } + const zoomScale = getZoomScale(stage); setStageBasedImagePosition({ imageBoundingBox, stage, @@ -287,8 +320,8 @@ export default function AnnotationViewer({ x: stage.x() + customStagePosition.x * zoomScale, y: stage.y() + customStagePosition.y * zoomScale, }, - }) - }, [customStagePosition]) + }); + }, [customStagePosition]); return (
- ) + ); } diff --git a/src/components/dynamicZoom.spec.tsx b/src/components/dynamicZoom.spec.tsx index 0d6da59..dc67b28 100644 --- a/src/components/dynamicZoom.spec.tsx +++ b/src/components/dynamicZoom.spec.tsx @@ -1,39 +1,44 @@ -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 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 type { AnnotationViewerProps, PointerPosition } from '@/common/types' +import type { AnnotationViewerProps, PointerPosition } from '@/common/types'; -import { getZoomScale } from '@/utils/zoom' +import { getZoomScale } from '@/utils/zoom'; -import AnnotationViewer from './AnnotationViewer' +import AnnotationViewer from './AnnotationViewer'; -const containerHeight = 700 -const containerWidth = 700 +const containerHeight = 700; +const containerWidth = 700; -interface AnnotationViewerWithDynamicZoomProps { - id: AnnotationViewerProps['id'] - data: AnnotationViewerProps['data'] -} +type AnnotationViewerWithDynamicZoomProps = { + 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 stage = stageObject.current - if (!stage) return - setCustomZoomLevel(modifier * getZoomScale(stage)) - } + const stage = stageObject.current; + if (!stage) { + return; + } + setCustomZoomLevel(modifier * getZoomScale(stage)); + }; const changePosition = (newPosition: PointerPosition) => { - setCustomStagePosition(newPosition) - } + setCustomStagePosition(newPosition); + }; return (
{ - changeScale(1.2) + changeScale(1.2); }} > Zoom in @@ -51,7 +56,7 @@ const AnnotationViewerWithDynamicZoom = ({
- ) -} + ); +}; describe('AnnotationViewer', () => { it('mount correctly', () => { @@ -111,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/index.ts b/src/index.ts index 3f5ef59..3c4c156 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import type { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; import type { AnnotationData, @@ -7,20 +7,20 @@ import type { 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 { drawLayer, drawShape, drawShapes, setShapeConfig, toBase64, -} from './utils/functions' -import getImagesFromPDF from './utils/getImagesFromPDF' -import getPDFPageCount from './utils/getPDFPageCount' -import { dataURItoBlob } from './utils/image' -import { getZoomScale } from './utils/zoom' +} from './utils/functions'; +import getImagesFromPDF from './utils/getImagesFromPDF'; +import getPDFPageCount from './utils/getPDFPageCount'; +import { dataURItoBlob } from './utils/image'; +import { getZoomScale } from './utils/zoom'; export type { Stage, @@ -30,7 +30,7 @@ export type { PointerPosition, Orientation, AnnotationData, -} +}; export { toBase64, drawShapes, @@ -43,4 +43,4 @@ export { drawShape, drawLayer, setShapeConfig, -} +}; diff --git a/src/utils/canvas.spec.ts b/src/utils/canvas.spec.ts index 6c244a2..ee1231d 100644 --- a/src/utils/canvas.spec.ts +++ b/src/utils/canvas.spec.ts @@ -1,28 +1,28 @@ -import type { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; -import type { 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, - ) + ); - 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 0989eb3..a1922e7 100644 --- a/src/utils/canvas.ts +++ b/src/utils/canvas.ts @@ -1,20 +1,22 @@ -import Konva from 'konva' -import type { Layer } from 'konva/lib/Layer' -import type { 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 { 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) => shapeByNode.get(node) +const shapeByNode = new WeakMap(); +export const getShapeFromNode = ( + node: Konva.Node, +): AnnotationShape | undefined => shapeByNode.get(node); export const mapShapesToPolygons = ( shapesLayer: Layer, @@ -25,22 +27,22 @@ 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 ?? {}), ...shape.config, - }) - shapeByNode.set(polygon, shape) - shapesLayer.add(polygon) + }); + shapeByNode.set(polygon, shape); + shapesLayer.add(polygon); if (useEvents) { bindEventToPolygon( polygon, @@ -48,10 +50,10 @@ export const mapShapesToPolygons = ( onClick, onShapeMouseEnter, onShapeMouseLeave, - ) + ); } - }) -} + }); +}; const bindEventToPolygon = ( polygon: Line, @@ -59,67 +61,75 @@ const bindEventToPolygon = ( onClick?: (shape: AnnotationShape) => void, onShapeMouseEnter?: (shape: AnnotationShape) => void, onShapeMouseLeave?: (shape: AnnotationShape) => void, -) => { - const stage = polygon.getStage() - const shape = shapeByNode.get(polygon) - if (!shape || !stage) return +): 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() ?? { 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) / @@ -131,5 +141,5 @@ export const getMousePosition = ( (oldScale * imageBoundingBox.height), 2, ), - } -} + }; +}; diff --git a/src/utils/functions.ts b/src/utils/functions.ts index bbcb79a..a27247f 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,58 +1,60 @@ -import Konva from 'konva' -import type { LineConfig } from 'konva/lib/shapes/Line' -import type { Stage } from 'konva/lib/Stage' +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'; /** Konva 8 types `findOne` as non-optional although it returns undefined on miss. */ -const findOne = (stage: Konva.Stage, selector: string) => - stage.findOne(selector) as Konva.Node | undefined +const findOne = ( + stage: Konva.Stage, + selector: string, +): Konva.Node | undefined => stage.findOne(selector); -export const drawLayer = (stage: Konva.Stage) => { - const shapesLayer = findOne(stage, `#${KONVA_REFS.shapesLayer}`) +export const drawLayer = (stage: Konva.Stage): void => { + const shapesLayer = findOne(stage, `#${KonvaRefs.ShapesLayer}`); if (shapesLayer instanceof Konva.Layer) { - shapesLayer.batchDraw() + 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 = findOne(stage, `#${id.toString()}`) +): void => { + const shape = findOne(stage, `#${id.toString()}`); if (shape instanceof Konva.Shape) { - shape.setAttrs(config) - shape.draw() + 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 = findOne(stage, `#${id.toString()}`) +): void => { + const shape = findOne(stage, `#${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, @@ -61,4 +63,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 8cf12d4..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: unknown) => { - expect((error as Error).name).to.equal('InvalidPDFException') - }) - }) + expect((error as Error).name).to.equal('InvalidPDFException'); + }); + }); it('should catch error when the PDF has too many Pages', () => { getImagesFromPDF(multiPage, 3).catch((error: unknown) => { - expect((error as Error).name).to.equal('TooManyPagesError') - }) - }) -}) + expect((error as Error).name).to.equal('TooManyPagesError'); + }); + }); +}); diff --git a/src/utils/getImagesFromPDF.ts b/src/utils/getImagesFromPDF.ts index b9b75bb..1c71933 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -1,33 +1,38 @@ -import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist' -import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist' +import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist'; +import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist'; -import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants' +import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants'; -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js` +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') +): Promise => { + const page: PDFPageProxy = await _document.getPage(pageNumber); + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); if (!context) { - throw new Error('Could not acquire a 2D canvas context') + throw new Error('Could not acquire a 2D canvas context'); } - 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 [, , width, height] = page.view; + if (width === undefined || height === undefined) { + throw new Error('Invalid PDF 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).promise.then(() => canvas.toDataURL()) -} + viewport, + }; + return await page + .render(renderContext) + .promise.then(() => canvas.toDataURL()); +}; export default async function getImagesFromPDF( file: string, @@ -35,19 +40,20 @@ export default async function getImagesFromPDF( onSuccess?: () => void, resolution?: number, ): Promise { - const pdf = await getDocument(file).promise + const pdf = await getDocument(file).promise; if (pdf.numPages > maxPages) { - const error = new Error('Too many pages') - error.name = 'TooManyPagesError' - throw error + const error = new Error('Too many pages'); + error.name = 'TooManyPagesError'; + throw error; } - onSuccess?.() + onSuccess?.(); const results = await Promise.allSettled( - Array.from({ length: pdf.numPages }, (_, index) => - getImageFromPage(pdf, index + 1, resolution), + Array.from( + { length: pdf.numPages }, + async (_, index) => await getImageFromPage(pdf, index + 1, resolution), ), - ) + ); return results.flatMap((result) => result.status === 'fulfilled' ? [result.value] : [], - ) + ); } diff --git a/src/utils/getPDFPageCount.spec.tsx b/src/utils/getPDFPageCount.spec.tsx index ce7961a..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: unknown) => { - expect((error as Error).name).to.equal('InvalidPDFException') - }) - }) -}) + expect((error as Error).name).to.equal('InvalidPDFException'); + }); + }); +}); diff --git a/src/utils/getPDFPageCount.ts b/src/utils/getPDFPageCount.ts index 63212e7..a8e3eb5 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,8 +1,8 @@ -import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist' +import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist'; -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js` +GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js`; -export default async function getPDFPageCount(file: string) { - const pdf = await getDocument(file).promise - return pdf.numPages +export default async function getPDFPageCount(file: string): Promise { + const pdf = await getDocument(file).promise; + return pdf.numPages; } diff --git a/src/utils/image.spec.ts b/src/utils/image.spec.ts index 8b261cb..85ca3d3 100644 --- a/src/utils/image.spec.ts +++ b/src/utils/image.spec.ts @@ -1,17 +1,17 @@ -import type { Stage } from 'konva/lib/Stage' +import type { Stage } from 'konva/lib/Stage'; -import type { ImageBoundingBox } from '@/common/types' +import type { ImageBoundingBox } from '@/common/types'; import { computeImageBoundingBox, setStageBasedImagePosition, -} from '@/utils/image' +} from '@/utils/image'; describe('image', () => { 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 +19,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 +43,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 18ec5a9..34b2e5f 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,112 +1,128 @@ -import heic2any from 'heic2any' -import type Konva from 'konva' -import type { Stage } from 'konva/lib/Stage' -import UTIF from 'utif' +import heic2any from 'heic2any'; +import type Konva from 'konva'; +import type { Stage } from 'konva/lib/Stage'; +import UTIF from 'utif'; import type { ImageBoundingBox, ImageData, PointerPosition, -} from '@/common/types' +} from '@/common/types'; -import { getZoomScale, setZoomScale } from '@/utils/zoom' +import { getZoomScale, setZoomScale } from '@/utils/zoom'; -export const dataURItoBlob = (dataURI: string) => { - let byteString - const splitDataURL = dataURI.split(',') - if (splitDataURL[0].includes('base64')) { - // atob decodes base64 data - byteString = atob(splitDataURL[1]) +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 }); +}; -export const prepareImage = async (image: string) => { - const blob = await urlToBlob(image) +export const prepareImage = async (image: string): Promise => { + const blob = await urlToBlob(image); if (blob.type === 'image/heic') { - return await heicToJpg(blob) + 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) + }).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 imageWidth = firstPageOfTif.width; + const imageHeight = firstPageOfTif.height; - const cnv = document.createElement('canvas') - cnv.width = imageWidth - cnv.height = imageHeight + const cnv = document.createElement('canvas'); + cnv.width = imageWidth; + cnv.height = imageHeight; - const ctx = cnv.getContext('2d') + const ctx = cnv.getContext('2d'); if (!ctx) { - throw new Error('Could not get 2D context') + throw new Error('Could not get 2D context'); } - const imageData = ctx.createImageData(imageWidth, imageHeight) - for (let i = 0; i < rgba.length; i++) { - imageData.data[i] = rgba[i] + 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 result = await heic2any({ blob, toType: 'image/jpeg' }); + // Multi-image HEIC files yield one Blob per image; only the first is shown. + const firstImage = Array.isArray(result) ? result[0] : result; + if (firstImage === undefined) { + throw new Error('HEIC file contains no image'); } - ctx.putImageData(imageData, 0, 0) - return new Promise((resolve, reject) => { - cnv.toBlob((blob) => { - if (blob) { - resolve(URL.createObjectURL(blob)) + 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(new Error('Could not encode TIFF canvas to a 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 { @@ -117,67 +133,67 @@ export const computeImageBoundingBox = ( 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 + resizeStage(stage, container); + const imageBoundingBox = computeImageBoundingBox(container, element); + const { x, y, width, height, scale } = imageBoundingBox; stage.scale({ x: scale, y: scale, - }) - setZoomScale(stage, 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 = getZoomScale(stage) - 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 62d3ccd..efc8f99 100644 --- a/src/utils/layer.ts +++ b/src/utils/layer.ts @@ -1,7 +1,7 @@ -import type { 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 22ad87c..45db711 100644 --- a/src/utils/orientation.ts +++ b/src/utils/orientation.ts @@ -1,93 +1,103 @@ -import type { Orientation } from '@/common/types' +import type { Orientation } from '@/common/types'; const EXIF_ORIENTATION: Record = { 0: 1, 90: 6, 180: 3, 270: 8, -} +}; -export const rotateImage = (image: string, degrees: Orientation = 0) => - applyRotation(image, EXIF_ORIENTATION[degrees]) +export const rotateImage = async ( + image: string, + degrees: Orientation = 0, +): Promise => { + if (degrees === 0) { + return image; + } + return await applyRotation(image, EXIF_ORIENTATION[degrees]); +}; -const applyRotation = (file: string, orientation: number) => - new Promise((resolve, reject) => { - 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')) - } + reject(new Error('Failed to load image for rotation')); + }; image.onload = () => { - const canvas = document.createElement('canvas') + const canvas = document.createElement('canvas'); const context = canvas.getContext('2d', { alpha: false, - }) + }); if (!context) { - reject(new Error('Could not acquire a 2D canvas context')) - return + reject(new Error('Could not acquire a 2D canvas context')); + return; } - const { width, height } = image + 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 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 + 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 + const sx = rightAligned ? canvas.width - outputCanvas.width : 0; + const sy = bottomAligned ? canvas.height - outputCanvas.height : 0; outputContext.drawImage( canvas, sx, @@ -98,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/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 ff9e382..1c310cc 100644 --- a/src/utils/selection.spec.ts +++ b/src/utils/selection.spec.ts @@ -1,6 +1,6 @@ -import type { 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 ad34a3e..3e4b234 100644 --- a/src/utils/selection.ts +++ b/src/utils/selection.ts @@ -1,112 +1,133 @@ -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 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 { KonvaRefs } from '@/common/constants'; import type { AnnotationShape, AnnotationViewerOptions, PointerPosition, -} from '@/common/types' +} from '@/common/types'; -import { getShapeFromNode } from '@/utils/canvas' +import { getShapeFromNode } from '@/utils/canvas'; -import { roundTo } from './roundTo' +import { roundTo } from './roundTo'; -const selectionAnchor = new WeakMap() +const selectionAnchor = new WeakMap(); -export const createSelectionRect = (options: AnnotationViewerOptions) => +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; + } - selectionAnchor.set(rect, firstPoint) - 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 - const anchor = selectionAnchor.get(rect) + if (!rect.visible()) { + return; + } + const anchor = selectionAnchor.get(rect); - if (!anchor) return + if (!anchor) { + return; + } - const secondPoint = calculateSelectionPoint(stage) + const secondPoint = calculateSelectionPoint(stage); - if (!secondPoint) return + if (!secondPoint) { + return; + } rect.setAttrs({ 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}`) - 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((node) => getShapeFromNode(node)) - .filter((shape) => shape !== undefined) + .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 a572db3..d30b519 100644 --- a/src/utils/useEventListener.ts +++ b/src/utils/useEventListener.ts @@ -1,23 +1,23 @@ -import { useEffect, useRef, type RefObject } from 'react' +import { useEffect, useRef, type RefObject } from 'react'; export default function useEventListener( eventName: K, handler: (event: WindowEventMap[K]) => void, element?: RefObject, -) { - const savedHandler = useRef(handler) +): void { + const savedHandler = useRef(handler); useEffect(() => { - savedHandler.current = handler - }) + savedHandler.current = handler; + }); useEffect(() => { - const target: HTMLElement | Window = element?.current ?? window - const listener = (event: Event) => { - savedHandler.current(event as WindowEventMap[K]) - } - target.addEventListener(eventName, listener) + const target: HTMLElement | Window = element?.current ?? window; + const listener = (event: Event): void => { + savedHandler.current(event as WindowEventMap[K]); + }; + target.addEventListener(eventName, listener); return () => { - target.removeEventListener(eventName, listener) - } - }, [eventName, element]) + target.removeEventListener(eventName, listener); + }; + }, [eventName, element]); } diff --git a/src/utils/useMultiSelection.ts b/src/utils/useMultiSelection.ts index d4db99f..779dc35 100644 --- a/src/utils/useMultiSelection.ts +++ b/src/utils/useMultiSelection.ts @@ -1,35 +1,35 @@ -import type { RefObject } from 'react' -import type Konva from 'konva' +import type { RefObject } from 'react'; +import type Konva from 'konva'; -import useEventListener from './useEventListener' +import useEventListener from './useEventListener'; -interface Props { - stageRef: RefObject - isSelectionActiveRef: RefObject -} +type Props = { + stageRef: RefObject; + isSelectionActiveRef: RefObject; +}; -const isSelectionModifier = (event: KeyboardEvent) => +const isSelectionModifier = (event: KeyboardEvent): boolean => event.ctrlKey || event.altKey || event.key === 'Control' || - event.key === 'Alt' + event.key === 'Alt'; export default function useMultiSelection({ stageRef, isSelectionActiveRef, -}: Props) { +}: Props): void { useEventListener('keydown', (event) => { - event.stopPropagation() + event.stopPropagation(); if (isSelectionModifier(event)) { - stageRef.current?.draggable(false) - isSelectionActiveRef.current = true + stageRef.current?.draggable(false); + isSelectionActiveRef.current = true; } - }) + }); useEventListener('keyup', (event) => { - event.stopPropagation() + event.stopPropagation(); if (isSelectionModifier(event)) { - stageRef.current?.draggable(true) - isSelectionActiveRef.current = false + stageRef.current?.draggable(true); + isSelectionActiveRef.current = false; } - }) + }); } diff --git a/src/utils/zoom.spec.ts b/src/utils/zoom.spec.ts index dab5a05..327c59a 100644 --- a/src/utils/zoom.spec.ts +++ b/src/utils/zoom.spec.ts @@ -1,39 +1,39 @@ -import type Konva from 'konva' +import type Konva from 'konva'; -import type { AnnotationViewerOptions } from '@/common/types' +import type { AnnotationViewerOptions } from '@/common/types'; import { calculateLensZoom, calculateStageZoom, calculateZoomScale, -} from '@/utils/zoom' +} 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,24 @@ 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 actual = calculateStageZoom( stage as Konva.Stage, deltaY, options as AnnotationViewerOptions, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) + expect(actual).to.deep.equal(expected); + }); + }); describe('calculateZoomScale', () => { it('should return the correct scale and position', () => { @@ -66,28 +66,28 @@ 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 actual = calculateZoomScale( stage as Konva.Stage, deltaY, imageBoundingBox, - ) + ); - expect(actual).to.deep.equal(expected) - }) - }) -}) + expect(actual).to.deep.equal(expected); + }); + }); +}); diff --git a/src/utils/zoom.ts b/src/utils/zoom.ts index 563862d..3bc0eb8 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -1,143 +1,153 @@ -import type Konva from 'konva' -import type { 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 { 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' +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 -} + const value: unknown = stage.getAttr(ZOOM_SCALE_ATTR); + return typeof value === 'number' ? value : 1; +}; -export const setZoomScale = (stage: Konva.Stage, zoomScale: number) => { - stage.setAttr(ZOOM_SCALE_ATTR, zoomScale) -} +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: roundTo(newScale, 2), 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 }) - setZoomScale(stage, 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 }) - setZoomScale(stage, 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, - } + }; - if (!options.zoom) return - 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: roundTo(newScale, 2), newPos }; +}; export const handleStageZoom = ( stage: Konva.Stage | null, imageBoundingBox: ImageBoundingBox | null, event: KonvaEventObject, options: AnnotationViewerOptions, -) => { - if (!stage || !imageBoundingBox || !options.zoom) return +): void => { + if (!stage || !imageBoundingBox || !options.zoom) { + return; + } - event.evt.preventDefault() + event.evt.preventDefault(); - const { max } = options.zoom + const { max } = options.zoom; - const stageZoom = calculateStageZoom(stage, event.evt.deltaY, options) - if (!stageZoom) return - const { newScale, newPos } = stageZoom + const stageZoom = calculateStageZoom(stage, event.evt.deltaY, options); + if (!stageZoom) { + return; + } + const { newScale, newPos } = stageZoom; - if (newScale > max) return + if (newScale > 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 }) - setZoomScale(stage, 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 }) - setZoomScale(stage, 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, @@ -146,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 cb7e200..676bf3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,12 @@ "jsx": "react-jsx", "types": ["node", "cypress"], "strict": true, - "isolatedModules": true, + "noUncheckedIndexedAccess": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "verbatimModuleSyntax": true, "esModuleInterop": true, "declaration": true, "outDir": "dist", diff --git a/vite.config.ts b/vite.config.ts index 7e3c08c..45bbcfb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,14 +1,12 @@ -import { fileURLToPath } from 'node:url' -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' -import dts from 'vite-plugin-dts' +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: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), - }, + tsconfigPaths: true, }, plugins: [ react(), @@ -29,4 +27,4 @@ export default defineConfig({ external: ['react', 'react-dom', 'react/jsx-runtime'], }, }, -}) +}); From 9fdbee7409de3b0e20a792a8c431fffea0534571 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:22:20 +0200 Subject: [PATCH 04/10] chg: :wrench: add prek and rehaul CI (#40) --- .github/workflows/_static-analysis.yml | 56 +++++++++++++ .github/workflows/_test-components.yml | 40 +++++++++ .github/workflows/_workflow-lint.yml | 28 +++++++ .github/workflows/deprecate-version.yml | 5 +- .github/workflows/pull-request.yml | 19 +++++ .github/workflows/tests.yml | 41 --------- .gitignore | 3 + .prettierignore | 3 + package.json | 1 + pnpm-lock.yaml | 105 ++++++++++++++++++++++++ prek.toml | 33 ++++++++ 11 files changed, 292 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/_static-analysis.yml create mode 100644 .github/workflows/_test-components.yml create mode 100644 .github/workflows/_workflow-lint.yml create mode 100644 .github/workflows/pull-request.yml delete mode 100644 .github/workflows/tests.yml create mode 100644 prek.toml diff --git a/.github/workflows/_static-analysis.yml b/.github/workflows/_static-analysis.yml new file mode 100644 index 0000000..f85e67a --- /dev/null +++ b/.github/workflows/_static-analysis.yml @@ -0,0 +1,56 @@ +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 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/_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..1bc0406 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,19 @@ +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 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 index 97809e4..00acc11 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,6 @@ node_modules pnpm-lock.yaml cypress/snapshots CHANGELOG.md + +# generated by Betterleaks in CI +betterleaks-report.sarif diff --git a/package.json b/package.json index 3b58c11..9592db5 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "devDependencies": { "@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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5b8602..7807099 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: '@ianvs/prettier-plugin-sort-imports': 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) @@ -416,6 +419,69 @@ packages: prettier-plugin-ember-template-tag: optional: true + '@j178/prek-android-arm64@0.5.4': + resolution: {integrity: sha512-K2kVaCpRmuQjr0sntQEMdTrAP2qTweWWYybyZyDwdIw0AC96Woo4zG7Tho0sJlSJcfcMVFWE34rgfhHB2NaRgA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@j178/prek-darwin-arm64@0.5.4': + resolution: {integrity: sha512-/dzoIpm/Zno+ZNRo4X6crvH5xmTZBieU+8yjXPkBFsiGSqZeN3HfoERQFAZ41C2k0OJnHdzOpnHKlBgAvHD5pg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@j178/prek-darwin-x64@0.5.4': + resolution: {integrity: sha512-k1j1CSXxG9MJtPW1IvJONoyLVlv2EKCVnJk73tvqnkKd3mkDq+x1cAiSbbEoGAtwMqhd7T9bnj5pGQENAaR/5A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@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] + + '@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] + + '@j178/prek-linux-x64-gnu@0.5.4': + resolution: {integrity: sha512-srSCqoDuq/dTs6sg+B193PmRqPsbT4ibY66KE/pkaAWOzWc6dSpECtsJQkymH172ewI4Gs9G5ZR8m7KCtqFiZA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@j178/prek-linux-x64-musl@0.5.4': + resolution: {integrity: sha512-nbJod1QxtJls9O+FLpwItrXm0aL8LGJlz0itdDgwvCem0IhSHnOikuyy16R1x/WA5AOVMA0EMbHcbivym+a7Mg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@j178/prek-win32-arm64-msvc@0.5.4': + resolution: {integrity: sha512-BqBWp6iBhxcPX+o5+v31xh1l6LYMqq/YomNd/LYvyX+QCL1Xptd2FgG5K0Zq2FG0C7y6isANFMifVdnoA3oKsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@j178/prek-win32-x64-msvc@0.5.4': + resolution: {integrity: sha512-p/1rouJZGcLq+MMHQ9PbfEvXT9r2iav2yB6anlH0FofbSbO5gzJUEsxVsuSF0yHvk5a6jnHiuS0IK5TDXdNKGA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@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==} @@ -2377,6 +2443,45 @@ snapshots: transitivePeerDependencies: - supports-color + '@j178/prek-android-arm64@0.5.4': + optional: true + + '@j178/prek-darwin-arm64@0.5.4': + optional: true + + '@j178/prek-darwin-x64@0.5.4': + optional: true + + '@j178/prek-linux-arm64-gnu@0.5.4': + optional: true + + '@j178/prek-linux-arm64-musl@0.5.4': + optional: true + + '@j178/prek-linux-x64-gnu@0.5.4': + optional: true + + '@j178/prek-linux-x64-musl@0.5.4': + optional: true + + '@j178/prek-win32-arm64-msvc@0.5.4': + optional: true + + '@j178/prek-win32-x64-msvc@0.5.4': + 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 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 diff --git a/prek.toml b/prek.toml new file mode 100644 index 0000000..0282df4 --- /dev/null +++ b/prek.toml @@ -0,0 +1,33 @@ +# 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]] +repo = "https://github.com/betterleaks/betterleaks" +rev = "v1.9.0" +hooks = [ + { id = "betterleaks" } +] From e779b5e92236cff6014d4bd8f7e18f11718a4e5b Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:39:26 +0200 Subject: [PATCH 05/10] chg: :arrow_up: update dependencies and add integration testing (#41) --- .github/workflows/_test-integration.yml | 40 +++ .github/workflows/pull-request.yml | 4 + cypress.config.ts | 13 +- cypress/integration/api.spec.ts | 55 ++++ cypress/integration/helpers.ts | 79 +++++ cypress/integration/images.spec.tsx | 124 ++++++++ cypress/integration/lens.spec.tsx | 123 ++++++++ cypress/integration/pdf.spec.tsx | 133 +++++++++ cypress/integration/viewer.spec.tsx | 370 ++++++++++++++++++++++++ package.json | 15 +- pnpm-lock.yaml | 145 +++++----- src/common/constants.ts | 3 +- src/common/types.ts | 6 + src/utils/functions.ts | 12 +- src/utils/getImagesFromPDF.ts | 18 +- src/utils/getPDFPageCount.ts | 6 +- src/utils/image.ts | 3 +- src/utils/pdf.ts | 28 ++ src/utils/pdfWorker.ts | 3 + src/utils/zoom.ts | 2 +- tsconfig.json | 2 +- vite.config.ts | 3 + 22 files changed, 1069 insertions(+), 118 deletions(-) create mode 100644 .github/workflows/_test-integration.yml create mode 100644 cypress/integration/api.spec.ts create mode 100644 cypress/integration/helpers.ts create mode 100644 cypress/integration/images.spec.tsx create mode 100644 cypress/integration/lens.spec.tsx create mode 100644 cypress/integration/pdf.spec.tsx create mode 100644 cypress/integration/viewer.spec.tsx create mode 100644 src/utils/pdf.ts create mode 100644 src/utils/pdfWorker.ts 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/pull-request.yml b/.github/workflows/pull-request.yml index 1bc0406..ccc7b67 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -17,3 +17,7 @@ jobs: 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/cypress.config.ts b/cypress.config.ts index 98ceb98..e19975e 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -20,13 +20,24 @@ export default defineConfig({ 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'], resolve: { tsconfigPaths: true }, + optimizeDeps: { + include: [ + 'react', + 'react-dom', + 'react-dom/client', + 'react/jsx-runtime', + ], + }, }), }, }, diff --git a/cypress/integration/api.spec.ts b/cypress/integration/api.spec.ts new file mode 100644 index 0000000..ac038b2 --- /dev/null +++ b/cypress/integration/api.spec.ts @@ -0,0 +1,55 @@ +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', + 'setShapeConfig', + 'toBase64', + ]; + + 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..f0c735b --- /dev/null +++ b/cypress/integration/helpers.ts @@ -0,0 +1,79 @@ +import type * as Library from '@/index'; + +export type DistLibrary = typeof Library; + +/** + * Path of the built entry point, relative to this file. It is kept in a + * variable so that neither tsc nor Vite resolve it ahead of time: the + * integration suite must stay type-checkable without a prior build, and the + * browser must receive the production chunks exactly as Rolldown emitted them. + */ +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, +}; + +/** + * Imports the built library (dist/) at runtime, as a consumer would, so that + * production-only behaviour such as lazy chunks and the inlined pdf.js worker + * is exercised rather than the TypeScript sources. + * + * Cypress keeps the same window for every test of a spec file, so module-level + * state inside dist/index.js (such as the memoised pdf.js worker) survives + * between tests. Pass `fresh: true` to cache-bust the entry point and get a + * brand new module instance when a test asserts on first-use behaviour. + */ +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)}`, + ); + }, + ), + ); +}; + +/** + * Names of the network resources fetched so far by the test window whose URL + * contains the given fragment. Used to prove that a lazy chunk is only + * downloaded once the feature that needs it is used. + * + * Resource entries accumulate for the whole spec file, so the "not yet + * loaded" assertion is only meaningful if no earlier test in the same file + * used the feature. + */ +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..71584ca --- /dev/null +++ b/cypress/integration/images.spec.tsx @@ -0,0 +1,124 @@ +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 by checking that the + * canvas pixels are not fully transparent. + */ +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). These paths rely on the production chunking, so they are + * verified against the built viewer. + */ +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..7cee748 --- /dev/null +++ b/cypress/integration/pdf.spec.tsx @@ -0,0 +1,133 @@ +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; + +/** + * pdf.js and its worker are shipped as lazy chunks and the worker is inlined + * as a blob. Those rewrites only happen in the production library build, so + * every expectation here runs 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); + }); + }); +}); diff --git a/cypress/integration/viewer.spec.tsx b/cypress/integration/viewer.spec.tsx new file mode 100644 index 0000000..153b883 --- /dev/null +++ b/cypress/integration/viewer.spec.tsx @@ -0,0 +1,370 @@ +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 and + * captures the Konva stage through the getStage prop so that tests can + * inspect the rendered scene graph directly. + */ +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/package.json b/package.json index 9592db5..e62bedb 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,14 @@ "CHANGELOG.md" ], "engines": { - "node": ">=22" + "node": ">=22.13" }, "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": { "@eslint/js": "^10.0.1", @@ -46,8 +45,7 @@ "@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", + "@types/utif": "^3.0.6", "@vitejs/plugin-react": "^6.1.1", "cypress": "^16.1.0", "eslint": "^10.11.0", @@ -73,7 +71,8 @@ "cy:prepare": "pnpm build", "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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7807099..640317c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,17 +175,14 @@ 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: ^4.2.67 - version: 4.10.38 + specifier: ^6.3.289 + version: 6.3.289 utif: specifier: ^3.1.0 version: 3.1.0 - uuid: - specifier: ^11.1.1 - version: 11.1.1 devDependencies: '@eslint/js': specifier: ^10.0.1 @@ -212,11 +209,8 @@ 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 '@vitejs/plugin-react': specifier: ^6.1.1 version: 6.1.1(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0)) @@ -522,79 +516,79 @@ packages: '@microsoft/tsdoc@0.17.0': resolution: {integrity: sha512-p68VexhnH7ojf3U27RdryUDqnKJgKaWUbCL96vJR7N0mqDuhKG5OIECk3sCPPdynxjk+yoVhRkScqtv5KQjzsw==} - '@napi-rs/canvas-android-arm64@0.1.100': - resolution: {integrity: sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==} + '@napi-rs/canvas-android-arm64@1.0.9': + resolution: {integrity: sha512-4LGXk2/0HVzE29K8SzML5WubgCp++B1FH3qgl35XmSZE+lLdr6P9VRQEnZ0MCLZMTSuJP41yyhtoiVNEuvrTIA==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.100': - resolution: {integrity: sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==} + '@napi-rs/canvas-darwin-arm64@1.0.9': + resolution: {integrity: sha512-YNdfLBzY0W/Pep9fo2L6RmoNlNksnn05LRnX66W63R3ij58S25QOTcjdtEt2v8+PnCESzqZsYzUo+QPeIR44NA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.100': - resolution: {integrity: sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==} + '@napi-rs/canvas-darwin-x64@1.0.9': + resolution: {integrity: sha512-ceZQSknTEcy3dOXoekv59LTCkXjvnLsq+VW5PeNNDHEPQbRS5Ervkm1EaDa7WLAjiYWMLuSQTRTHao1dEX4prg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': - resolution: {integrity: sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==} + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.9': + resolution: {integrity: sha512-XhfI0Wwv4llhd6nnWDtY3kQKjq0r+y1i91PlJlJI24ag2U9WrnwbG1qS3+fDLEyouwEVFchiKkTEHozK+5iUNA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': - resolution: {integrity: sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==} + '@napi-rs/canvas-linux-arm64-gnu@1.0.9': + resolution: {integrity: sha512-012oiYtKaE7i9oxc8q7nraT7kDOpLcaCmFLzVe9Ty34RHDdoDzbWLrVh827CNxYh/EADX1eSikA3ymLjo/nNuw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-arm64-musl@0.1.100': - resolution: {integrity: sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==} + '@napi-rs/canvas-linux-arm64-musl@1.0.9': + resolution: {integrity: sha512-Ls5UWYFFn63casTZEczbeyEg3vRDRkv9lscuGwfchtY5yLLQhgOB8SN4YGxmfJ5vTaBwZ2YUxBS3NtmjJmFXdA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': - resolution: {integrity: sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==} + '@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] - '@napi-rs/canvas-linux-x64-gnu@0.1.100': - resolution: {integrity: sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==} + '@napi-rs/canvas-linux-x64-gnu@1.0.9': + resolution: {integrity: sha512-6kaz3w0QMy77PDWk6rJ1ksIihdad3qzEyX2o2oGT8GwCaypfT5mhjr8buOO5hstyLxcWXDScuz56RsINLtBPIQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-x64-musl@0.1.100': - resolution: {integrity: sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==} + '@napi-rs/canvas-linux-x64-musl@1.0.9': + resolution: {integrity: sha512-xrGvmS3v55hmZ86ls/kBLVNMUTYio3f6Ik0DireemG994VfPAwiA3ZXA0Uf1bByctkB3NQ1Sfb+H5bkdUnnzfQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': - resolution: {integrity: sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==} + '@napi-rs/canvas-win32-arm64-msvc@1.0.9': + resolution: {integrity: sha512-yjmVS3ArZeRVCP7jqbPq4rpZa/BhTeI7ELE2XqJg3snICQBDevLZyArxswHkiTnT34KRic33/4fLirrHI+SY8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@napi-rs/canvas-win32-x64-msvc@0.1.100': - resolution: {integrity: sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==} + '@napi-rs/canvas-win32-x64-msvc@1.0.9': + resolution: {integrity: sha512-QlSYQdMQslB81nlABo9wNfQ6npFhE7/O+saCZdqVGueGanyRk4jCogD5EwQenfP3kIq9e+mm6GreQBjX5MrA8g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.100': - resolution: {integrity: sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==} + '@napi-rs/canvas@1.0.9': + resolution: {integrity: sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ==} engines: {node: '>= 10'} '@oxc-project/types@0.151.0': @@ -784,9 +778,6 @@ packages: '@types/utif@3.0.6': resolution: {integrity: sha512-4XHDJkLHoUeLA2P/VOd6dpDJpSTpV84tN9EyHojZrW22jtUR0VrdLwVRUAw+oI3g72vSzOo+YQOzfwcW6WYHLw==} - '@types/uuid@8.3.4': - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - '@typescript-eslint/eslint-plugin@8.71.0': resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1535,8 +1526,16 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - konva@8.4.3: - resolution: {integrity: sha512-ARqdgAbdNIougRlOKvkQwHlGhXPRBV4KvhCP+qoPpGoVQwwiJe4Hkdu4HHdRPb9rGUp04jDTAxBzEwBsE272pg==} + 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==} @@ -1754,9 +1753,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdfjs-dist@4.10.38: - resolution: {integrity: sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==} - engines: {node: '>=20'} + 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==} @@ -2123,10 +2122,6 @@ packages: utif@3.1.0: resolution: {integrity: sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==} - uuid@11.1.1: - resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} - hasBin: true - verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} @@ -2544,52 +2539,52 @@ snapshots: '@microsoft/tsdoc@0.17.0': {} - '@napi-rs/canvas-android-arm64@0.1.100': + '@napi-rs/canvas-android-arm64@1.0.9': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.100': + '@napi-rs/canvas-darwin-arm64@1.0.9': optional: true - '@napi-rs/canvas-darwin-x64@0.1.100': + '@napi-rs/canvas-darwin-x64@1.0.9': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.9': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + '@napi-rs/canvas-linux-arm64-gnu@1.0.9': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.100': + '@napi-rs/canvas-linux-arm64-musl@1.0.9': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + '@napi-rs/canvas-linux-riscv64-gnu@1.0.9': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.100': + '@napi-rs/canvas-linux-x64-gnu@1.0.9': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.100': + '@napi-rs/canvas-linux-x64-musl@1.0.9': optional: true - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + '@napi-rs/canvas-win32-arm64-msvc@1.0.9': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.100': + '@napi-rs/canvas-win32-x64-msvc@1.0.9': optional: true - '@napi-rs/canvas@0.1.100': + '@napi-rs/canvas@1.0.9': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.100 - '@napi-rs/canvas-darwin-arm64': 0.1.100 - '@napi-rs/canvas-darwin-x64': 0.1.100 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.100 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.100 - '@napi-rs/canvas-linux-arm64-musl': 0.1.100 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-musl': 0.1.100 - '@napi-rs/canvas-win32-arm64-msvc': 0.1.100 - '@napi-rs/canvas-win32-x64-msvc': 0.1.100 + '@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 '@oxc-project/types@0.151.0': {} @@ -2732,8 +2727,6 @@ snapshots: dependencies: '@types/node': 24.19.0 - '@types/uuid@8.3.4': {} - '@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 @@ -3503,7 +3496,7 @@ snapshots: kolorist@1.8.0: {} - konva@8.4.3: {} + konva@10.7.0: {} levn@0.4.1: dependencies: @@ -3691,9 +3684,9 @@ snapshots: pathe@2.0.3: {} - pdfjs-dist@4.10.38: + pdfjs-dist@6.3.289: optionalDependencies: - '@napi-rs/canvas': 0.1.100 + '@napi-rs/canvas': 1.0.9 pend@1.2.0: {} @@ -4034,8 +4027,6 @@ snapshots: dependencies: pako: 1.0.11 - uuid@11.1.1: {} - verror@1.10.0: dependencies: assert-plus: 1.0.0 diff --git a/src/common/constants.ts b/src/common/constants.ts index f938924..a1c1ab8 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -38,7 +38,8 @@ export const DEFAULT_ANNOTATION_VIEWER_OPTIONS = { }, onMouseEnter: (polygon: Line): void => { const stroke = polygon.stroke(); - polygon.setAttr('fill', `${stroke}40`); + const fill = typeof stroke === 'string' ? `${stroke}40` : stroke; + polygon.setAttr('fill', fill); polygon.draw(); }, shapeConfig: { diff --git a/src/common/types.ts b/src/common/types.ts index 0989944..db8bea7 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -29,8 +29,14 @@ export type AnnotationViewerOptions = BaseOptions & { 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?: { + /** Multiplier applied per wheel step. */ modifier: number; + /** Highest magnification reachable through the wheel. */ max: number; defaultZoom: number; }; diff --git a/src/utils/functions.ts b/src/utils/functions.ts index a27247f..1b6ed94 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -4,14 +4,8 @@ import type { Stage } from 'konva/lib/Stage'; import { KonvaRefs } from '@/common/constants'; -/** Konva 8 types `findOne` as non-optional although it returns undefined on miss. */ -const findOne = ( - stage: Konva.Stage, - selector: string, -): Konva.Node | undefined => stage.findOne(selector); - export const drawLayer = (stage: Konva.Stage): void => { - const shapesLayer = findOne(stage, `#${KonvaRefs.ShapesLayer}`); + const shapesLayer = stage.findOne(`#${KonvaRefs.ShapesLayer}`); if (shapesLayer instanceof Konva.Layer) { shapesLayer.batchDraw(); } else { @@ -24,7 +18,7 @@ export const drawShape = ( id: string | number, config: LineConfig, ): void => { - const shape = findOne(stage, `#${id.toString()}`); + const shape = stage.findOne(`#${id.toString()}`); if (shape instanceof Konva.Shape) { shape.setAttrs(config); shape.draw(); @@ -48,7 +42,7 @@ export const setShapeConfig = ( id: string | number, config: LineConfig, ): void => { - const shape = findOne(stage, `#${id.toString()}`); + const shape = stage.findOne(`#${id.toString()}`); if (shape) { shape.setAttrs(config); } else { diff --git a/src/utils/getImagesFromPDF.ts b/src/utils/getImagesFromPDF.ts index 1c71933..4b00061 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -1,9 +1,8 @@ import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist'; -import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist'; import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants'; -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js`; +import { loadPdfDocument } from './pdf'; const getImageFromPage = async ( _document: PDFDocumentProxy, @@ -12,10 +11,6 @@ const getImageFromPage = async ( ): Promise => { const page: PDFPageProxy = await _document.getPage(pageNumber); const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - if (!context) { - throw new Error('Could not acquire a 2D canvas context'); - } const [, , width, height] = page.view; if (width === undefined || height === undefined) { throw new Error('Invalid PDF page view'); @@ -25,13 +20,8 @@ const getImageFromPage = async ( const viewport = page.getViewport({ scale: safeScale }); canvas.height = viewport.height; canvas.width = viewport.width; - const renderContext = { - canvasContext: context, - viewport, - }; - return await page - .render(renderContext) - .promise.then(() => canvas.toDataURL()); + await page.render({ canvas, viewport }).promise; + return canvas.toDataURL(); }; export default async function getImagesFromPDF( @@ -40,7 +30,7 @@ export default async function getImagesFromPDF( onSuccess?: () => void, resolution?: number, ): Promise { - const pdf = await getDocument(file).promise; + const pdf = await loadPdfDocument(file); if (pdf.numPages > maxPages) { const error = new Error('Too many pages'); error.name = 'TooManyPagesError'; diff --git a/src/utils/getPDFPageCount.ts b/src/utils/getPDFPageCount.ts index a8e3eb5..3dfbc69 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,8 +1,6 @@ -import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist'; - -GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${version}/pdf.worker.js`; +import { loadPdfDocument } from './pdf'; export default async function getPDFPageCount(file: string): Promise { - const pdf = await getDocument(file).promise; + const pdf = await loadPdfDocument(file); return pdf.numPages; } diff --git a/src/utils/image.ts b/src/utils/image.ts index 34b2e5f..9eacac4 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -1,4 +1,3 @@ -import heic2any from 'heic2any'; import type Konva from 'konva'; import type { Stage } from 'konva/lib/Stage'; import UTIF from 'utif'; @@ -79,8 +78,8 @@ export const tiffToJpg = async (blob: Blob): Promise => { }; export const heicToJpg = async (blob: Blob): Promise => { + const { default: heic2any } = await import('heic2any'); const result = await heic2any({ blob, toType: 'image/jpeg' }); - // Multi-image HEIC files yield one Blob per image; only the first is shown. const firstImage = Array.isArray(result) ? result[0] : result; if (firstImage === undefined) { throw new Error('HEIC file contains no image'); diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts new file mode 100644 index 0000000..0bd1c68 --- /dev/null +++ b/src/utils/pdf.ts @@ -0,0 +1,28 @@ +import type * as Pdfjs from 'pdfjs-dist'; +import type { PDFDocumentProxy } from 'pdfjs-dist'; + +let pdfjsModule: 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. + */ +const loadPdfjs = async (): Promise => { + pdfjsModule ??= Promise.all([import('pdfjs-dist'), import('./pdfWorker')]) + .then(([pdfjs, { createPdfWorker }]) => { + pdfjs.GlobalWorkerOptions.workerPort = createPdfWorker(); + return pdfjs; + }) + .catch((error: unknown) => { + pdfjsModule = undefined; + throw error; + }); + return await pdfjsModule; +}; + +export const loadPdfDocument = async ( + file: string, +): Promise => { + const { getDocument } = await loadPdfjs(); + return await getDocument({ url: file }).promise; +}; 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/zoom.ts b/src/utils/zoom.ts index 3bc0eb8..e48b9ed 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -130,7 +130,7 @@ export const handleStageZoom = ( } const { newScale, newPos } = stageZoom; - if (newScale > max) { + if (newScale / imageBoundingBox.scale > max) { return; } diff --git a/tsconfig.json b/tsconfig.json index 676bf3d..318602a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "module": "ESNext", "moduleResolution": "bundler", "jsx": "react-jsx", - "types": ["node", "cypress"], + "types": ["node", "cypress", "vite/client"], "strict": true, "noUncheckedIndexedAccess": true, "noImplicitReturns": true, diff --git a/vite.config.ts b/vite.config.ts index 45bbcfb..2bc2584 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -17,6 +17,9 @@ export default defineConfig({ bundleTypes: true, }), ], + worker: { + format: 'es', + }, build: { lib: { entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)), From b7c5e7d782518e4edd8ab7d11440e82942609cc8 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:24:20 +0200 Subject: [PATCH 06/10] feat: :sparkles: add support for page lazy-loading (#42) --- README.md | 68 +++ cypress/integration/api.spec.ts | 2 + cypress/integration/pdf.spec.tsx | 29 ++ docs/docs/API/annotation-viewer-api.mdx | 8 +- docs/docs/Tutorial/pdf-file.md | 55 ++- docs/docs/Utils/draw-layer.mdx | 2 +- docs/docs/Utils/draw-shape.mdx | 2 +- docs/docs/Utils/get-images-form-pdf-api.mdx | 18 +- docs/docs/Utils/get-pdf-page-count.mdx | 11 +- docs/docs/Utils/open-pdf.mdx | 102 ++++ docs/docs/Utils/set-shape-config.mdx | 2 +- docs/docs/Utils/use-pdf-document.mdx | 105 ++++ docs/docs/customization.md | 2 +- docs/docs/intro.md | 12 +- docs/docs/migration.md | 34 +- eslint.config.ts | 1 + src/common/types.ts | 1 + src/hooks/usePDFDocument.spec.tsx | 493 +++++++++++++++++++ src/hooks/usePDFDocument.ts | 395 +++++++++++++++ src/index.ts | 29 ++ src/utils/getImagesFromPDF.ts | 73 ++- src/utils/getPDFPageCount.ts | 18 +- src/utils/image.spec.ts | 31 ++ src/utils/image.ts | 9 +- src/utils/pdf.spec.ts | 415 ++++++++++++++++ src/utils/pdf.ts | 505 +++++++++++++++++++- 26 files changed, 2331 insertions(+), 91 deletions(-) create mode 100644 docs/docs/Utils/open-pdf.mdx create mode 100644 docs/docs/Utils/use-pdf-document.mdx create mode 100644 src/hooks/usePDFDocument.spec.tsx create mode 100644 src/hooks/usePDFDocument.ts create mode 100644 src/utils/pdf.spec.ts diff --git a/README.md b/README.md index 3376ce3..e0a61d7 100644 --- a/README.md +++ b/README.md @@ -91,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/integration/api.spec.ts b/cypress/integration/api.spec.ts index ac038b2..a016afc 100644 --- a/cypress/integration/api.spec.ts +++ b/cypress/integration/api.spec.ts @@ -15,8 +15,10 @@ describe('built library public API', () => { 'getImagesFromPDF', 'getPDFPageCount', 'getZoomScale', + 'openPDF', 'setShapeConfig', 'toBase64', + 'usePDFDocument', ]; it('exposes exactly the documented runtime exports, all of them functions', () => { diff --git a/cypress/integration/pdf.spec.tsx b/cypress/integration/pdf.spec.tsx index 7cee748..9e6499c 100644 --- a/cypress/integration/pdf.spec.tsx +++ b/cypress/integration/pdf.spec.tsx @@ -130,4 +130,33 @@ describe('built library PDF support', () => { 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/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..f1890bc --- /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` and aborts any pending render 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..a90f581 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. 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 index 6c97a3b..4d835d3 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -112,6 +112,7 @@ export default defineConfig( 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', diff --git a/src/common/types.ts b/src/common/types.ts index db8bea7..f98081e 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -38,6 +38,7 @@ export type AnnotationViewerOptions = BaseOptions & { 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; }; }; 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..db2a239 --- /dev/null +++ b/src/hooks/usePDFDocument.ts @@ -0,0 +1,395 @@ +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`. + * Changing it clears `pages` and re-renders every page that was loaded (or + * loading) at the new value; renders are cached per resolution, so + * switching back is instant. + */ + 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. A slot is `undefined` until the page + * has been rendered, then holds an image URL usable as `data.image`. + */ + pages: (string | undefined)[]; + /** Pages (0-based indexes) whose render is queued or in progress. */ + loading: ReadonlySet; + /** + * Ensures the page at `index` (0-based) and the rest of its batch are + * rendered. Safe to call repeatedly: already rendered pages are skipped and + * a page still waiting in the queue is promoted. The page itself is + * requested with high priority so it is drawn before any pending prefetch. + */ + loadPage: (index: number) => void; + /** + * Renders every page in `[from, to)` (0-based integers, `to` exclusive). + * Bounds are clamped to the document; non-integer bounds are ignored. + */ + 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. A change of either starts a new generation: the previous + * generation's pending renders are aborted and its callbacks ignored, so a + * closed document cannot touch the state of its replacement. + */ +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: 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 is destroyed + * (and its object URLs revoked) when `source` changes or on unmount. + * + * @example + * const { pages, numPages, loadPage } = usePDFDocument(file); + * + * // when the user scrolls page `index` into view: + * loadPage(index); + */ +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 3c4c156..74cbfa2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,12 @@ import type { } 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, @@ -20,6 +26,16 @@ import { 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 { @@ -30,6 +46,16 @@ export type { PointerPosition, Orientation, AnnotationData, + OpenPDFOptions, + PDFDocumentHandle, + PDFPageOptions, + PDFPageOutput, + PDFPagePriority, + PDFRenderedPage, + PDFSource, + UsePDFDocumentOptions, + UsePDFDocumentResult, + UsePDFDocumentStatus, }; export { toBase64, @@ -37,9 +63,12 @@ export { 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/getImagesFromPDF.ts b/src/utils/getImagesFromPDF.ts index 4b00061..c41194a 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -1,49 +1,40 @@ -import type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist'; - -import { MAX_PDF_SCALE, PDF_RESOLUTION } from '@/common/constants'; - -import { loadPdfDocument } from './pdf'; - -const getImageFromPage = async ( - _document: PDFDocumentProxy, - pageNumber: number, - resolution = PDF_RESOLUTION, -): Promise => { - const page: PDFPageProxy = await _document.getPage(pageNumber); - const canvas = document.createElement('canvas'); - const [, , width, height] = page.view; - if (width === undefined || height === undefined) { - throw new Error('Invalid PDF 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; - await page.render({ canvas, viewport }).promise; - return canvas.toDataURL(); -}; +import { openPDF, type PDFSource } from './pdf'; +/** + * Renders every page of a PDF to a PNG data URL. Pages that fail to render + * are skipped rather than failing the whole call, so the result may be + * shorter than the page count. + * + * @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: string, + file: PDFSource, maxPages = Infinity, onSuccess?: () => void, resolution?: number, ): Promise { - const pdf = await loadPdfDocument(file); - if (pdf.numPages > maxPages) { - const error = new Error('Too many pages'); - error.name = 'TooManyPagesError'; - throw error; + 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(); } - onSuccess?.(); - const results = await Promise.allSettled( - Array.from( - { length: pdf.numPages }, - async (_, index) => await getImageFromPage(pdf, index + 1, resolution), - ), - ); - return results.flatMap((result) => - result.status === 'fulfilled' ? [result.value] : [], - ); } diff --git a/src/utils/getPDFPageCount.ts b/src/utils/getPDFPageCount.ts index 3dfbc69..3c23268 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,6 +1,16 @@ -import { loadPdfDocument } from './pdf'; +import { openPDF, type PDFSource } from './pdf'; -export default async function getPDFPageCount(file: string): Promise { - const pdf = await loadPdfDocument(file); - return pdf.numPages; +/** + * Counts the pages of a PDF. If you also need to render pages, use `openPDF` + * instead so the document is only parsed once. + */ +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 85ca3d3..ce4f9df 100644 --- a/src/utils/image.spec.ts +++ b/src/utils/image.spec.ts @@ -1,13 +1,44 @@ +import demoHeic from 'cypress/assets/demo.heic'; import type { Stage } from 'konva/lib/Stage'; import type { ImageBoundingBox } from '@/common/types'; import { computeImageBoundingBox, + prepareImage, setStageBasedImagePosition, } 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 }; diff --git a/src/utils/image.ts b/src/utils/image.ts index 9eacac4..5c55cbc 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -33,9 +33,16 @@ export const dataURItoBlob = (dataURI: string): Blob => { 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): Promise => { const blob = await urlToBlob(image); - if (blob.type === 'image/heic') { + if (HEIF_MIME_TYPES.has(blob.type)) { return await heicToJpg(blob); } if (blob.type === 'image/tiff') { 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 index 0bd1c68..1765de6 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -1,28 +1,503 @@ import type * as Pdfjs from 'pdfjs-dist'; -import type { PDFDocumentProxy } from 'pdfjs-dist'; +import type { + PDFDocumentProxy, + PDFPageProxy, + PDFWorker, + RenderTask, +} from 'pdfjs-dist'; -let pdfjsModule: Promise | undefined; +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 => { - pdfjsModule ??= Promise.all([import('pdfjs-dist'), import('./pdfWorker')]) - .then(([pdfjs, { createPdfWorker }]) => { - pdfjs.GlobalWorkerOptions.workerPort = createPdfWorker(); - return pdfjs; - }) +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) => { - pdfjsModule = undefined; + runtime = undefined; throw error; }); - return await pdfjsModule; + 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; }; -export const loadPdfDocument = async ( - file: string, -): Promise => { - const { getDocument } = await loadPdfjs(); - return await getDocument({ url: file }).promise; +/** 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(); + }, + }; }; From 87fcb06c730289468259234a5bfd79f0e2062b39 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:46:50 +0200 Subject: [PATCH 07/10] doc: :memo: update documentation and Readme --- PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md | 0 docs/docs/Utils/use-pdf-document.mdx | 2 +- docs/docs/intro.md | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) 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/docs/docs/Utils/use-pdf-document.mdx b/docs/docs/Utils/use-pdf-document.mdx index f1890bc..39918ef 100644 --- a/docs/docs/Utils/use-pdf-document.mdx +++ b/docs/docs/Utils/use-pdf-document.mdx @@ -85,7 +85,7 @@ The hook is **0-based** (array semantics); the handle is **1-based** (pdf.js sem ::: :::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` and aborts any pending render 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). +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 diff --git a/docs/docs/intro.md b/docs/docs/intro.md index a90f581..7098bd5 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -107,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) From ac94fd4e3638382ea47df3de2e61fa180738035e Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:56:16 +0200 Subject: [PATCH 08/10] chore: :wrench: add license checking to the repo --- .github/workflows/_static-analysis.yml | 3 + CONTRIBUTING.md | 2 +- package.json | 5 +- pnpm-lock.yaml | 1002 ++++++++++++++++++++++++ prek.toml | 8 + 5 files changed, 1018 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_static-analysis.yml b/.github/workflows/_static-analysis.yml index f85e67a..3995ad3 100644 --- a/.github/workflows/_static-analysis.yml +++ b/.github/workflows/_static-analysis.yml @@ -54,3 +54,6 @@ jobs: - name: Lint run: pnpm lint + + - name: Check dependency licenses + run: pnpm run license:check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eef7680..ab56acc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/package.json b/package.json index e62bedb..f471cf4 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "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", @@ -67,6 +68,7 @@ }, "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", "build": "vite build", @@ -82,7 +84,8 @@ "prettier:check": "prettier --check .", "prettier:write": "prettier --write .", "lint": "tsc --noEmit && eslint --max-warnings 0 . && prettier --check .", - "lint:fix": "eslint --fix . && prettier --write ." + "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 640317c..d8915c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,9 @@ importers: 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: ^3.9.9 version: 3.9.9 @@ -375,6 +378,10 @@ packages: 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'} @@ -413,6 +420,13 @@ packages: prettier-plugin-ember-template-tag: optional: true + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@isaacs/string-locale-compare@1.1.0': + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} + '@j178/prek-android-arm64@0.5.4': resolution: {integrity: sha512-K2kVaCpRmuQjr0sntQEMdTrAP2qTweWWYybyZyDwdIw0AC96Woo4zG7Tho0sJlSJcfcMVFWE34rgfhHB2NaRgA==} engines: {node: '>=18'} @@ -591,6 +605,64 @@ packages: resolution: {integrity: sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ==} engines: {node: '>= 10'} + '@npmcli/agent@4.0.2': + resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/arborist@9.6.0': + resolution: {integrity: sha512-Dku9UWbrrX+UCu8rQ1obGKaQAL4kwdt3hHCNXrd0n0R/4B8oq3CzloUAShwFjfsAGM6KY27gPuNftOUEZ4nhOw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/git@7.0.2': + resolution: {integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/installed-package-contents@4.0.0': + resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + '@npmcli/map-workspaces@5.0.3': + resolution: {integrity: sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/metavuln-calculator@9.0.3': + resolution: {integrity: sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/name-from-folder@4.0.0': + resolution: {integrity: sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/node-gyp@5.0.0': + resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/package-json@7.0.5': + resolution: {integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/promise-spawn@9.0.1': + resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/query@5.0.0': + resolution: {integrity: sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/redact@4.0.0': + resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/run-script@10.0.4': + resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} + engines: {node: ^20.17.0 || >=22.9.0} + '@oxc-project/types@0.151.0': resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} @@ -735,11 +807,43 @@ packages: 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: cypress: '>=15.10.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==} @@ -867,6 +971,14 @@ packages: typescript: optional: true + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + 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: @@ -877,6 +989,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -923,6 +1039,10 @@ packages: argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + array-find-index@1.0.2: + resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} + engines: {node: '>=0.10.0'} + asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -958,6 +1078,10 @@ 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==} @@ -976,6 +1100,10 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + 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==} @@ -1001,6 +1129,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-remote-interface@0.33.3: resolution: {integrity: sha512-zNnn0prUL86Teru6UCAZ1yU1XeXljHl3gj7OrfPcarEfU62OUU4IujDPdTDW3dAWwRqN3ZMG/Chhkh2gPL/wiw==} hasBin: true @@ -1021,6 +1153,10 @@ packages: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} + 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'} @@ -1046,6 +1182,10 @@ packages: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} + 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'} @@ -1059,6 +1199,10 @@ packages: confbox@0.3.1: resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} + 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==} @@ -1069,6 +1213,11 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 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==} @@ -1135,6 +1284,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -1234,6 +1387,9 @@ packages: resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} engines: {node: '>=4'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + exsolve@1.1.1: resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} @@ -1292,6 +1448,10 @@ packages: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} + 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==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1334,6 +1494,10 @@ packages: 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'} @@ -1391,17 +1555,40 @@ packages: hookified@2.2.0: resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + 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@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.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'} @@ -1422,6 +1609,14 @@ packages: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + ip-address@10.7.3: + resolution: {integrity: sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==} + engines: {node: '>= 12'} + is-core-module@2.17.0: resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} engines: {node: '>= 0.4'} @@ -1464,6 +1659,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} + isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} @@ -1494,6 +1693,10 @@ packages: engines: {node: '>=6'} hasBin: true + 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==} @@ -1506,6 +1709,9 @@ packages: 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==} @@ -1517,9 +1723,18 @@ packages: 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==} + just-diff-apply@5.5.0: + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} + + just-diff@6.0.2: + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} + keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} @@ -1541,6 +1756,11 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + 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'} @@ -1627,6 +1847,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} @@ -1641,12 +1864,20 @@ packages: 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==} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + 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==} engines: {node: '>= 0.4'} @@ -1681,6 +1912,43 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + 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-sized@2.0.0: + resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + 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 + mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -1695,10 +1963,57 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.1.0: + resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} + engines: {node: '>=18'} + + 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@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 + + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + npm-bundled@5.0.0: + resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-install-checks@8.0.0: + resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-normalize-package-bin@5.0.0: + resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-package-arg@13.0.2: + resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-packlist@10.0.4: + resolution: {integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-pick-manifest@11.0.3: + resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-registry-fetch@19.1.1: + resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} + engines: {node: ^20.17.0 || >=22.9.0} + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -1733,9 +2048,22 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-map@7.0.8: + resolution: {integrity: sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw==} + engines: {node: '>=18'} + + 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==} + 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==} @@ -1750,6 +2078,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1792,6 +2124,10 @@ packages: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.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==} engines: {node: ^10 || ^12 || >=14} @@ -1809,10 +2145,24 @@ packages: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} + 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'} + 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==} @@ -1843,6 +2193,10 @@ packages: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} + read-cmd-shim@6.0.0: + resolution: {integrity: sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==} + engines: {node: ^20.17.0 || >=22.9.0} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -1926,6 +2280,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sigstore@4.1.1: + resolution: {integrity: sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==} + engines: {node: ^20.17.0 || >=22.9.0} + slice-ansi@7.1.2: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} @@ -1934,6 +2292,18 @@ packages: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.10: + resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1942,6 +2312,30 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + spdx-compare@1.0.0: + resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + 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==} + + 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==} @@ -1953,6 +2347,10 @@ packages: ssim.js@3.5.0: resolution: {integrity: sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==} + 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'} @@ -1999,6 +2397,10 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} + engines: {node: '>=18'} + throttleit@1.0.1: resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} @@ -2025,12 +2427,24 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + treeify@1.1.0: + resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} + engines: {node: '>=0.6'} + + treeverse@3.0.0: + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: 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==} @@ -2068,6 +2482,10 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici@6.29.0: + resolution: {integrity: sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==} + engines: {node: '>=18.17'} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -2122,6 +2540,13 @@ packages: utif@3.1.0: resolution: {integrity: sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + 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==} @@ -2185,6 +2610,10 @@ packages: vscode-uri@3.2.0: resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} @@ -2193,6 +2622,11 @@ packages: engines: {node: '>= 8'} hasBin: true + 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'} @@ -2204,6 +2638,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + 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==} engines: {node: '>=8.3.0'} @@ -2219,6 +2657,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yauzl@3.4.0: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} engines: {node: '>=12'} @@ -2411,6 +2856,8 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 + '@gar/promise-retry@1.0.3': {} + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -2438,6 +2885,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@isaacs/string-locale-compare@1.1.0': {} + '@j178/prek-android-arm64@0.5.4': optional: true @@ -2587,6 +3040,124 @@ snapshots: '@napi-rs/canvas-win32-x64-msvc': 1.0.9 optional: true + '@npmcli/agent@4.0.2(supports-color@8.1.1)': + dependencies: + 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 + + '@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 + + '@npmcli/fs@5.0.0': + dependencies: + semver: 7.8.5 + + '@npmcli/git@7.0.2': + dependencies: + '@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 + which: 6.0.1 + + '@npmcli/installed-package-contents@4.0.0': + dependencies: + npm-bundled: 5.0.0 + npm-normalize-package-bin: 5.0.0 + + '@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 + + '@npmcli/metavuln-calculator@9.0.3(supports-color@8.1.1)': + dependencies: + 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: + - supports-color + + '@npmcli/name-from-folder@4.0.0': {} + + '@npmcli/node-gyp@5.0.0': {} + + '@npmcli/package-json@7.0.5': + dependencies: + '@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 + + '@npmcli/promise-spawn@9.0.1': + dependencies: + which: 6.0.1 + + '@npmcli/query@5.0.0': + dependencies: + postcss-selector-parser: 7.1.6 + + '@npmcli/redact@4.0.0': {} + + '@npmcli/run-script@10.0.4': + dependencies: + '@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 + '@oxc-project/types@0.151.0': {} '@rolldown/binding-android-arm-eabi@1.2.11': @@ -2683,6 +3254,38 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@sigstore/bundle@4.0.0': + dependencies: + '@sigstore/protobuf-specs': 0.5.2 + + '@sigstore/core@3.2.1': {} + + '@sigstore/protobuf-specs@0.5.2': {} + + '@sigstore/sign@4.1.1(supports-color@8.1.1)': + dependencies: + '@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 + + '@sigstore/tuf@4.0.2(supports-color@8.1.1)': + dependencies: + '@sigstore/protobuf-specs': 0.5.2 + tuf-js: 4.1.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@sigstore/verify@3.1.1': + dependencies: + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.2 + '@simonsmith/cypress-image-snapshot@11.0.0(cypress@16.1.0)': dependencies: '@types/pixelmatch': 5.2.6 @@ -2693,6 +3296,13 @@ snapshots: transitivePeerDependencies: - jest + '@tufjs/canonical-json@2.0.0': {} + + '@tufjs/models@4.1.0': + dependencies: + '@tufjs/canonical-json': 2.0.0 + minimatch: 10.2.6 + '@types/argparse@1.0.38': {} '@types/esrecurse@4.3.1': {} @@ -2837,12 +3447,18 @@ snapshots: optionalDependencies: typescript: 6.0.3 + abbrev@2.0.0: {} + + abbrev@4.0.0: {} + acorn-jsx@5.3.2(acorn@8.18.0): dependencies: acorn: 8.18.0 acorn@8.18.0: {} + agent-base@7.1.4: {} + ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -2892,6 +3508,8 @@ snapshots: dependencies: sprintf-js: 1.0.3 + array-find-index@1.0.2: {} + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 @@ -2916,6 +3534,14 @@ snapshots: dependencies: tweetnacl: 0.14.5 + bin-links@6.0.2: + dependencies: + 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 + blob-util@2.0.2: {} bluebird@3.7.2: {} @@ -2937,6 +3563,19 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.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: '@cacheable/memory': 2.2.0 @@ -2966,6 +3605,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chownr@3.0.0: {} + chrome-remote-interface@0.33.3: dependencies: commander: 2.11.0 @@ -2991,6 +3632,8 @@ snapshots: slice-ansi: 8.0.0 string-width: 8.3.0 + cmd-shim@8.0.0: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3010,6 +3653,8 @@ snapshots: commander@6.2.1: {} + common-ancestor-path@2.0.0: {} + common-tags@1.8.2: {} compare-versions@6.1.1: {} @@ -3018,6 +3663,8 @@ snapshots: confbox@0.3.1: {} + content-type@2.1.0: {} + convert-source-map@2.0.0: {} core-util-is@1.0.2: {} @@ -3028,6 +3675,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cssesc@3.0.0: {} + csstype@3.2.3: {} cypress@16.1.0: @@ -3122,6 +3771,8 @@ snapshots: dependencies: once: 1.4.0 + env-paths@2.2.1: {} + environment@1.1.0: {} es-define-property@1.0.1: {} @@ -3246,6 +3897,8 @@ snapshots: dependencies: pify: 2.3.0 + exponential-backoff@3.1.3: {} + exsolve@1.1.1: {} extend@3.0.2: {} @@ -3304,6 +3957,10 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + fsevents@2.3.3: optional: true @@ -3347,6 +4004,12 @@ snapshots: glob-to-regexp@0.4.1: {} + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + global-dirs@3.0.1: dependencies: ini: 2.0.0 @@ -3392,16 +4055,45 @@ snapshots: hookified@2.2.0: {} + hosted-git-info@9.0.3: + dependencies: + 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.4.0: dependencies: assert-plus: 1.0.0 jsprim: 2.0.2 sshpk: 1.18.0 + https-proxy-agent@7.0.6(supports-color@8.1.1): + dependencies: + 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.7.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + ieee754@1.2.1: {} + ignore-walk@8.0.0: + dependencies: + minimatch: 10.2.6 + ignore@5.3.2: {} ignore@7.0.10: {} @@ -3412,6 +4104,10 @@ snapshots: ini@2.0.0: {} + ini@6.0.0: {} + + ip-address@10.7.3: {} + is-core-module@2.17.0: dependencies: hasown: 2.0.4 @@ -3443,6 +4139,8 @@ snapshots: isexe@2.0.0: {} + isexe@4.0.0: {} + isstream@0.1.2: {} jest-image-snapshot@6.5.2: @@ -3465,6 +4163,8 @@ snapshots: jsesc@3.1.0: {} + json-parse-even-better-errors@5.0.0: {} + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -3473,6 +4173,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-nice@1.1.4: {} + json-stringify-safe@5.0.1: {} json5@2.2.3: {} @@ -3483,6 +4185,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + jsprim@2.0.2: dependencies: assert-plus: 1.0.0 @@ -3490,6 +4194,10 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 + just-diff-apply@5.5.0: {} + + just-diff@6.0.2: {} + keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -3503,6 +4211,23 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + license-checker-rseidelsohn@5.0.1(supports-color@8.1.1): + dependencies: + '@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 @@ -3571,6 +4296,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.clonedeep@4.5.0: {} + lodash.once@4.1.1: {} lodash@4.18.1: {} @@ -3588,6 +4315,8 @@ snapshots: 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 @@ -3596,6 +4325,23 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 + 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: {} @@ -3620,6 +4366,42 @@ snapshots: minimist@1.2.8: {} + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + 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 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@2.0.0: + dependencies: + minipass: 7.1.3 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mkdirp@1.0.4: {} + mlly@1.8.2: dependencies: acorn: 8.18.0 @@ -3633,8 +4415,75 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.1.0: + dependencies: + content-type: 2.1.0 + + node-gyp@12.4.0: + dependencies: + 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 + tar: 7.5.22 + tinyglobby: 0.2.17 + undici: 6.29.0 + which: 6.0.1 + node-releases@2.0.57: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + nopt@9.0.0: + dependencies: + abbrev: 4.0.0 + + npm-bundled@5.0.0: + dependencies: + npm-normalize-package-bin: 5.0.0 + + npm-install-checks@8.0.0: + dependencies: + semver: 7.8.5 + + npm-normalize-package-bin@5.0.0: {} + + npm-package-arg@13.0.2: + dependencies: + hosted-git-info: 9.0.3 + proc-log: 6.1.0 + semver: 7.8.5 + validate-npm-package-name: 7.0.2 + + npm-packlist@10.0.4: + dependencies: + ignore-walk: 8.0.0 + proc-log: 6.1.0 + + 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 + + npm-registry-fetch@19.1.1(supports-color@8.1.1): + dependencies: + '@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 + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -3672,8 +4521,38 @@ snapshots: dependencies: p-limit: 3.1.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: {} + parse-conflict-json@5.0.1: + dependencies: + 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@4.0.0: {} @@ -3682,6 +4561,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + pathe@2.0.3: {} pdfjs-dist@6.3.289: @@ -3718,6 +4602,11 @@ snapshots: pngjs@6.0.0: {} + postcss-selector-parser@7.1.6: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.5.28: dependencies: nanoid: 3.3.19 @@ -3730,8 +4619,16 @@ snapshots: pretty-bytes@5.6.0: {} + proc-log@6.1.0: {} + process@0.11.10: {} + proggy@4.0.0: {} + + promise-all-reject-late@1.0.1: {} + + promise-call-limit@3.0.2: {} + proxy-from-env@1.0.0: {} pump@3.0.4: @@ -3759,6 +4656,8 @@ snapshots: react@19.3.0: {} + read-cmd-shim@6.0.0: {} + request-progress@3.0.0: dependencies: throttleit: 1.0.1 @@ -3855,6 +4754,17 @@ snapshots: signal-exit@4.1.0: {} + sigstore@4.1.1(supports-color@8.1.1): + dependencies: + '@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@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -3865,10 +4775,58 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5(supports-color@8.1.1): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + socks: 2.8.10 + transitivePeerDependencies: + - supports-color + + socks@2.8.10: + dependencies: + ip-address: 10.7.3 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} source-map@0.6.1: {} + 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: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.24 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + 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: {} + + spdx-ranges@2.1.1: {} + + spdx-satisfies@6.0.0: + dependencies: + spdx-compare: 1.0.0 + spdx-expression-parse: 3.0.1 + spdx-ranges: 2.1.1 + sprintf-js@1.0.3: {} sshpk@1.18.0: @@ -3885,6 +4843,10 @@ snapshots: ssim.js@3.5.0: {} + ssri@13.0.1: + dependencies: + minipass: 7.1.3 + string-argv@0.3.2: {} string-width@4.2.3: @@ -3926,6 +4888,14 @@ snapshots: systeminformation@5.33.14: {} + tar@7.5.22: + dependencies: + '@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: {} tinyglobby@0.2.17: @@ -3947,10 +4917,22 @@ snapshots: tree-kill@1.2.2: {} + treeify@1.1.0: {} + + treeverse@3.0.0: {} + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 + tuf-js@4.1.0(supports-color@8.1.1): + dependencies: + '@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: safe-buffer: 5.2.1 @@ -3982,6 +4964,8 @@ snapshots: undici-types@7.24.6: {} + undici@6.29.0: {} + universalify@2.0.1: {} 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.1(@types/node@24.19.0)(jiti@2.7.0)): @@ -4027,6 +5011,10 @@ snapshots: dependencies: pako: 1.0.11 + util-deprecate@1.0.2: {} + + validate-npm-package-name@7.0.2: {} + verror@1.10.0: dependencies: assert-plus: 1.0.0 @@ -4063,12 +5051,18 @@ snapshots: vscode-uri@3.2.0: {} + walk-up-path@4.0.0: {} + webpack-virtual-modules@0.6.2: {} which@2.0.2: dependencies: isexe: 2.0.0 + which@6.0.1: + dependencies: + isexe: 4.0.0 + word-wrap@1.2.5: {} wrap-ansi@9.0.2: @@ -4079,10 +5073,18 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + ws@7.5.13: {} yallist@3.1.1: {} + yallist@4.0.0: {} + + yallist@5.0.0: {} + yauzl@3.4.0: dependencies: pend: 1.2.0 diff --git a/prek.toml b/prek.toml index 0282df4..690c5a1 100644 --- a/prek.toml +++ b/prek.toml @@ -25,6 +25,14 @@ 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" From 0b5ca10da6270efc589ae9a246d2f8c55fe57204 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:33:20 +0200 Subject: [PATCH 09/10] fix tests --- cypress/integration/helpers.ts | 21 ++---------- cypress/integration/images.spec.tsx | 6 ++-- cypress/integration/pdf.spec.tsx | 4 +-- cypress/integration/viewer.spec.tsx | 4 +-- src/components/AnnotationLens.spec.tsx | 44 ++++++++++++++++++++++++++ src/components/AnnotationLens.tsx | 6 ++-- src/components/AnnotationViewer.tsx | 6 +++- src/hooks/usePDFDocument.ts | 28 +++------------- src/utils/getImagesFromPDF.ts | 3 +- src/utils/getPDFPageCount.ts | 3 +- src/utils/useMultiSelection.ts | 24 ++++++++++---- src/utils/zoom.spec.ts | 17 ++++------ src/utils/zoom.ts | 4 +-- 13 files changed, 93 insertions(+), 77 deletions(-) diff --git a/cypress/integration/helpers.ts b/cypress/integration/helpers.ts index f0c735b..cde28d5 100644 --- a/cypress/integration/helpers.ts +++ b/cypress/integration/helpers.ts @@ -3,10 +3,7 @@ import type * as Library from '@/index'; export type DistLibrary = typeof Library; /** - * Path of the built entry point, relative to this file. It is kept in a - * variable so that neither tsc nor Vite resolve it ahead of time: the - * integration suite must stay type-checkable without a prior build, and the - * browser must receive the production chunks exactly as Rolldown emitted them. + * Path of the built entry point, relative to this file. */ const distEntry = '../../dist/index.js'; @@ -18,14 +15,7 @@ export const CONTAINER_STYLE = { }; /** - * Imports the built library (dist/) at runtime, as a consumer would, so that - * production-only behaviour such as lazy chunks and the inlined pdf.js worker - * is exercised rather than the TypeScript sources. - * - * Cypress keeps the same window for every test of a spec file, so module-level - * state inside dist/index.js (such as the memoised pdf.js worker) survives - * between tests. Pass `fresh: true` to cache-bust the entry point and get a - * brand new module instance when a test asserts on first-use behaviour. + * Loads the built library so that it is kept between tests. */ export const loadDist = ( options: { fresh?: boolean } = {}, @@ -46,13 +36,8 @@ export const loadDist = ( }; /** - * Names of the network resources fetched so far by the test window whose URL - * contains the given fragment. Used to prove that a lazy chunk is only + * Used to prove that a lazy chunk is only * downloaded once the feature that needs it is used. - * - * Resource entries accumulate for the whole spec file, so the "not yet - * loaded" assertion is only meaningful if no earlier test in the same file - * used the feature. */ export const loadedResources = ( win: Cypress.AUTWindow, diff --git a/cypress/integration/images.spec.tsx b/cypress/integration/images.spec.tsx index 71584ca..01e0cd3 100644 --- a/cypress/integration/images.spec.tsx +++ b/cypress/integration/images.spec.tsx @@ -8,8 +8,7 @@ import { CONTAINER_STYLE, loadDist, loadedResources } from './helpers'; const containerId = 'annotationViewer'; /** - * Asserts that the viewer ended up with a drawn image by checking that the - * canvas pixels are not fully transparent. + * Asserts that the viewer ended up with a drawn image. */ const expectPaintedCanvas = (): void => { cy.get(`#${containerId} canvas`) @@ -33,8 +32,7 @@ const expectPaintedCanvas = (): void => { /** * Format conversion runs through utif (TIFF) and a lazily loaded heic2any - * chunk (HEIC). These paths rely on the production chunking, so they are - * verified against the built viewer. + * chunk (HEIC). */ describe('built library image formats', () => { it('displays a JPEG image', () => { diff --git a/cypress/integration/pdf.spec.tsx b/cypress/integration/pdf.spec.tsx index 9e6499c..afdd892 100644 --- a/cypress/integration/pdf.spec.tsx +++ b/cypress/integration/pdf.spec.tsx @@ -6,9 +6,7 @@ import { imageDimensions, loadDist } from './helpers'; const PAGE_COUNT = 5; /** - * pdf.js and its worker are shipped as lazy chunks and the worker is inlined - * as a blob. Those rewrites only happen in the production library build, so - * every expectation here runs against dist/ rather than the sources. + * 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', () => { diff --git a/cypress/integration/viewer.spec.tsx b/cypress/integration/viewer.spec.tsx index 153b883..f9a9fb9 100644 --- a/cypress/integration/viewer.spec.tsx +++ b/cypress/integration/viewer.spec.tsx @@ -14,9 +14,7 @@ const EMPTY_AREA = { x: 10, y: 10 }; type MountResult = { stage: () => Stage }; /** - * Mounts the built AnnotationViewer with the demo image and shapes and - * captures the Konva stage through the getStage prop so that tests can - * inspect the rendered scene graph directly. + * Mounts the built AnnotationViewer with the demo image and shapes. */ const mountViewer = ( lib: DistLibrary, diff --git a/src/components/AnnotationLens.spec.tsx b/src/components/AnnotationLens.spec.tsx index 3c12d30..71862bb 100644 --- a/src/components/AnnotationLens.spec.tsx +++ b/src/components/AnnotationLens.spec.tsx @@ -1,6 +1,8 @@ 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 type { AnnotationData, PointerPosition } from '@/common/types'; @@ -173,4 +175,46 @@ describe('AnnotationLens', () => { ); }); }); + + 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 3329799..b83379e 100644 --- a/src/components/AnnotationLens.tsx +++ b/src/components/AnnotationLens.tsx @@ -24,7 +24,7 @@ import type { } from '@/common/types'; import { mapShapesToPolygons } from '@/utils/canvas'; -import { handleResizeImage } from '@/utils/image'; +import { handleResizeImage, prepareImage } from '@/utils/image'; import { clearLayers } from '@/utils/layer'; import { rotateImage } from '@/utils/orientation'; import { handleLensZoom } from '@/utils/zoom'; @@ -111,7 +111,9 @@ export default function AnnotationLens({ const { signal } = controller; void (async () => { try { - const src = await rotateImage(image, orientation); + const prepared = await prepareImage(image); + signal.throwIfAborted(); + const src = await rotateImage(prepared, orientation); if (signal.aborted) { return; } diff --git a/src/components/AnnotationViewer.tsx b/src/components/AnnotationViewer.tsx index 7892f7f..6b7aefa 100644 --- a/src/components/AnnotationViewer.tsx +++ b/src/components/AnnotationViewer.tsx @@ -127,7 +127,11 @@ export default function AnnotationViewer({ }; }); - useMultiSelection({ stageRef: stageObject, isSelectionActiveRef }); + useMultiSelection({ + stageRef: stageObject, + isSelectionActiveRef, + isEnabled: options.enableSelection === true, + }); const fitImageToContainer = (): void => { const imageBoundingBox = handleResizeImage( diff --git a/src/hooks/usePDFDocument.ts b/src/hooks/usePDFDocument.ts index db2a239..a33cbb3 100644 --- a/src/hooks/usePDFDocument.ts +++ b/src/hooks/usePDFDocument.ts @@ -20,9 +20,7 @@ export type UsePDFDocumentOptions = { batch?: number; /** * Rendering resolution in pixels, see `PDFPageOptions.resolution`. - * Changing it clears `pages` and re-renders every page that was loaded (or - * loading) at the new value; renders are cached per resolution, so - * switching back is instant. + * Re-renders on change. */ resolution?: number; /** Rejects documents with more pages, see `OpenPDFOptions.maxPages`. */ @@ -36,22 +34,17 @@ export type UsePDFDocumentResult = { error: Error | null; numPages: number; /** - * One slot per page, indexed from 0. A slot is `undefined` until the page - * has been rendered, then holds an image URL usable as `data.image`. + * One slot per page, indexed from 0. */ pages: (string | undefined)[]; /** Pages (0-based indexes) whose render is queued or in progress. */ loading: ReadonlySet; /** - * Ensures the page at `index` (0-based) and the rest of its batch are - * rendered. Safe to call repeatedly: already rendered pages are skipped and - * a page still waiting in the queue is promoted. The page itself is - * requested with high priority so it is drawn before any pending prefetch. + * Renders a batch starting at `index` (0-based). */ loadPage: (index: number) => void; /** * Renders every page in `[from, to)` (0-based integers, `to` exclusive). - * Bounds are clamped to the document; non-integer bounds are ignored. */ loadRange: (from: number, to: number) => void; /** Underlying handle for advanced use, `null` until `status` is `ready`. */ @@ -78,9 +71,7 @@ type State = { /** * Pages requested so far, tagged with the document and resolution they were - * requested for. A change of either starts a new generation: the previous - * generation's pending renders are aborted and its callbacks ignored, so a - * closed document cannot touch the state of its replacement. + * requested for. */ type Requested = { document: PDFDocumentHandle | null; @@ -148,16 +139,7 @@ const assertInteger = (name: string, value: number, min: number): void => { }; /** - * Opens a 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 is destroyed - * (and its object URLs revoked) when `source` changes or on unmount. - * - * @example - * const { pages, numPages, loadPage } = usePDFDocument(file); - * - * // when the user scrolls page `index` into view: - * loadPage(index); + * Opens a PDF and renders its pages progressively. */ export const usePDFDocument = ( source: PDFSource | null | undefined, diff --git a/src/utils/getImagesFromPDF.ts b/src/utils/getImagesFromPDF.ts index c41194a..33a19ea 100644 --- a/src/utils/getImagesFromPDF.ts +++ b/src/utils/getImagesFromPDF.ts @@ -2,8 +2,7 @@ import { openPDF, type PDFSource } from './pdf'; /** * Renders every page of a PDF to a PNG data URL. Pages that fail to render - * are skipped rather than failing the whole call, so the result may be - * shorter than the page count. + * are skipped. * * @deprecated Renders and holds all pages in memory at once. Use `openPDF` * (or the `usePDFDocument` hook) to render pages progressively instead. diff --git a/src/utils/getPDFPageCount.ts b/src/utils/getPDFPageCount.ts index 3c23268..ceb2bee 100644 --- a/src/utils/getPDFPageCount.ts +++ b/src/utils/getPDFPageCount.ts @@ -1,8 +1,7 @@ import { openPDF, type PDFSource } from './pdf'; /** - * Counts the pages of a PDF. If you also need to render pages, use `openPDF` - * instead so the document is only parsed once. + * Counts the pages of a PDF. */ export default async function getPDFPageCount( file: PDFSource, diff --git a/src/utils/useMultiSelection.ts b/src/utils/useMultiSelection.ts index 779dc35..dc6052c 100644 --- a/src/utils/useMultiSelection.ts +++ b/src/utils/useMultiSelection.ts @@ -1,4 +1,4 @@ -import type { RefObject } from 'react'; +import { useRef, type RefObject } from 'react'; import type Konva from 'konva'; import useEventListener from './useEventListener'; @@ -6,6 +6,7 @@ import useEventListener from './useEventListener'; type Props = { stageRef: RefObject; isSelectionActiveRef: RefObject; + isEnabled: boolean; }; const isSelectionModifier = (event: KeyboardEvent): boolean => @@ -17,19 +18,28 @@ const isSelectionModifier = (event: KeyboardEvent): boolean => export default function useMultiSelection({ stageRef, isSelectionActiveRef, + isEnabled, }: Props): void { + const wasDraggableRef = useRef(false); + useEventListener('keydown', (event) => { event.stopPropagation(); - if (isSelectionModifier(event)) { - stageRef.current?.draggable(false); - isSelectionActiveRef.current = true; + 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 (isSelectionModifier(event)) { - stageRef.current?.draggable(true); - isSelectionActiveRef.current = false; + 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 327c59a..dc308ce 100644 --- a/src/utils/zoom.spec.ts +++ b/src/utils/zoom.spec.ts @@ -45,10 +45,7 @@ describe('zoom', () => { 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, @@ -56,7 +53,9 @@ describe('zoom', () => { 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); }); }); @@ -76,10 +75,7 @@ describe('zoom', () => { }; 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, @@ -87,7 +83,8 @@ describe('zoom', () => { 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 e48b9ed..9da970a 100644 --- a/src/utils/zoom.ts +++ b/src/utils/zoom.ts @@ -47,7 +47,7 @@ export const calculateZoomScale = ( y: roundTo(pointerPosition.y - mousePointTo.y * newScale, 2), }; - return { newScale: roundTo(newScale, 2), newPos }; + return { newScale, newPos }; }; export const handleZoomScale = ( @@ -107,7 +107,7 @@ export const calculateStageZoom = ( y: roundTo(pointerPosition.y - mousePointTo.y * newScale, 2), }; - return { newScale: roundTo(newScale, 2), newPos }; + return { newScale, newPos }; }; export const handleStageZoom = ( From d845faf3376024c36c52743e2fcd162d0ff459a7 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:30:50 +0100 Subject: [PATCH 10/10] :arrow_up: bump dependencies --- pnpm-lock.yaml | 286 ++++++++++++++++++++++++++++++++++++-------- pnpm-workspace.yaml | 6 + 2 files changed, 239 insertions(+), 53 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8915c7..d12fdc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,9 +163,15 @@ settings: 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: @@ -213,7 +219,7 @@ importers: version: 3.0.6 '@vitejs/plugin-react': specifier: ^6.1.1 - version: 6.1.1(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0)) + version: 6.1.1(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0)) cypress: specifier: ^16.1.0 version: 16.1.0 @@ -251,11 +257,11 @@ importers: 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: ^8.3.1 - version: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) + specifier: ^8.3.3 + version: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) vite-plugin-dts: 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.1(@types/node@24.19.0)(jiti@2.7.0)) + 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: @@ -666,42 +672,81 @@ packages: '@oxc-project/types@0.151.0': resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} + '@oxc-project/types@0.155.0': + resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==} + '@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] + '@rolldown/binding-android-arm-eabi@1.2.15': + resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + '@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] + '@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] + '@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] + '@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] + '@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] + '@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] + '@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] + '@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] + '@rolldown/binding-linux-arm64-gnu@1.2.11': resolution: {integrity: sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -709,6 +754,13 @@ packages: os: [linux] libc: [glibc] + '@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] + '@rolldown/binding-linux-arm64-musl@1.2.11': resolution: {integrity: sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -716,6 +768,13 @@ packages: 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} @@ -723,6 +782,13 @@ packages: 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} @@ -730,6 +796,13 @@ packages: 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} @@ -737,6 +810,13 @@ packages: 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} @@ -744,24 +824,49 @@ packages: 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==} @@ -948,7 +1053,7 @@ packages: '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 babel-plugin-react-compiler: ^1.0.0 oxc-transform-react: ^0.145.0 - vite: ^8.0.0 + vite: ^8.3.3 peerDependenciesMeta: '@rolldown/plugin-babel': optional: true @@ -1408,8 +1513,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.8: - resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} + fast-uri@3.1.9: + resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -1613,8 +1718,8 @@ packages: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} - ip-address@10.7.3: - resolution: {integrity: sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==} + ip-address@10.7.5: + resolution: {integrity: sha512-t2v0rkILYv9WXmSL7TpAaU3HXgaFYOPUcH3QHgwQ++gpJuH3bqyMy4Fvt/DJCTKwzH3Cf+bAp5W/mFL39KPLzA==} engines: {node: '>= 12'} is-core-module@2.17.0: @@ -1955,8 +2060,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - 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 @@ -2128,8 +2233,8 @@ packages: 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: @@ -2221,6 +2326,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.2.15: + resolution: {integrity: sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@3.30.0: resolution: {integrity: sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -2304,8 +2414,8 @@ packages: resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} source-map@0.6.1: @@ -2482,8 +2592,8 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.29.0: - resolution: {integrity: sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==} + undici@6.29.1: + resolution: {integrity: sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==} engines: {node: '>=18.17'} universalify@2.0.1: @@ -2500,7 +2610,7 @@ packages: rolldown: '*' rollup: '>=3' typescript: '>=4' - vite: '>=3' + vite: ^8.3.3 webpack: ^4 || ^5 peerDependenciesMeta: '@microsoft/api-extractor': @@ -2555,7 +2665,7 @@ packages: peerDependencies: '@microsoft/api-extractor': '>=7' rollup: '>=3' - vite: '>=3' + vite: ^8.3.3 peerDependenciesMeta: '@microsoft/api-extractor': optional: true @@ -2564,13 +2674,13 @@ packages: vite: optional: true - vite@8.3.1: - resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==} + vite@8.3.5: + resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.7.1 + '@vitejs/devtools': ^0.7.1 || ^0.8.0 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -2676,10 +2786,10 @@ packages: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} peerDependencies: - zod: ^3.25.0 || ^4.0.0 + zod: ^4.6.6 - zod@4.6.5: - resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} snapshots: @@ -3158,53 +3268,101 @@ snapshots: node-gyp: 12.4.0 proc-log: 6.1.0 - '@oxc-project/types@0.151.0': {} + '@oxc-project/types@0.151.0': + optional: true + + '@oxc-project/types@0.155.0': {} '@rolldown/binding-android-arm-eabi@1.2.11': optional: true + '@rolldown/binding-android-arm-eabi@1.2.15': + optional: true + '@rolldown/binding-android-arm64@1.2.11': optional: true + '@rolldown/binding-android-arm64@1.2.15': + optional: true + '@rolldown/binding-darwin-arm64@1.2.11': optional: true + '@rolldown/binding-darwin-arm64@1.2.15': + optional: true + '@rolldown/binding-darwin-x64@1.2.11': optional: true + '@rolldown/binding-darwin-x64@1.2.15': + optional: true + '@rolldown/binding-freebsd-x64@1.2.11': optional: true + '@rolldown/binding-freebsd-x64@1.2.15': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.11': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.15': + optional: true + '@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.1': {} '@rollup/pluginutils@5.4.0(rollup@3.30.0)': @@ -3428,10 +3586,10 @@ snapshots: '@typescript-eslint/types': 8.71.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@6.1.1(vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0))': + '@vitejs/plugin-react@6.1.1(vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) '@volar/language-core@2.4.28': dependencies: @@ -3477,14 +3635,14 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.8 + fast-uri: 3.1.9 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.8 + fast-uri: 3.1.9 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -3804,8 +3962,8 @@ snapshots: '@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.5 - zod-validation-error: 4.0.2(zod@4.6.5) + zod: 4.6.7 + zod-validation-error: 4.0.2(zod@4.6.7) transitivePeerDependencies: - supports-color @@ -3911,7 +4069,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.8: {} + fast-uri@3.1.9: {} fdir@6.5.0(picomatch@4.0.7): optionalDependencies: @@ -4106,7 +4264,7 @@ snapshots: ini@6.0.0: {} - ip-address@10.7.3: {} + ip-address@10.7.5: {} is-core-module@2.17.0: dependencies: @@ -4411,7 +4569,7 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.19: {} + nanoid@3.3.20: {} natural-compare@1.4.0: {} @@ -4429,7 +4587,7 @@ snapshots: semver: 7.8.5 tar: 7.5.22 tinyglobby: 0.2.17 - undici: 6.29.0 + undici: 6.29.1 which: 6.0.1 node-releases@2.0.57: {} @@ -4607,11 +4765,11 @@ snapshots: 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: {} @@ -4698,6 +4856,28 @@ snapshots: '@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 + + rolldown@1.2.15: + dependencies: + '@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: @@ -4787,10 +4967,10 @@ snapshots: socks@2.8.10: dependencies: - ip-address: 10.7.3 + ip-address: 10.7.5 smart-buffer: 4.2.0 - source-map-js@1.2.1: {} + source-map-js@1.2.2: {} source-map@0.6.1: {} @@ -4964,11 +5144,11 @@ snapshots: undici-types@7.24.6: {} - undici@6.29.0: {} + undici@6.29.1: {} universalify@2.0.1: {} - 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.1(@types/node@24.19.0)(jiti@2.7.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) @@ -4984,7 +5164,7 @@ snapshots: '@microsoft/api-extractor': 7.59.3(@types/node@24.19.0) rolldown: 1.2.11 rollup: 3.30.0 - vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) transitivePeerDependencies: - supports-color @@ -5021,13 +5201,13 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - 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.1(@types/node@24.19.0)(jiti@2.7.0)): + 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: - 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.1(@types/node@24.19.0)(jiti@2.7.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)) optionalDependencies: '@microsoft/api-extractor': 7.59.3(@types/node@24.19.0) rollup: 3.30.0 - vite: 8.3.1(@types/node@24.19.0)(jiti@2.7.0) + vite: 8.3.5(@types/node@24.19.0)(jiti@2.7.0) transitivePeerDependencies: - '@rspack/core' - '@vue/language-core' @@ -5037,12 +5217,12 @@ snapshots: - typescript - webpack - vite@8.3.1(@types/node@24.19.0)(jiti@2.7.0): + vite@8.3.5(@types/node@24.19.0)(jiti@2.7.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 - postcss: 8.5.28 - rolldown: 1.2.11 + postcss: 8.5.31 + rolldown: 1.2.15 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.19.0 @@ -5091,8 +5271,8 @@ snapshots: yocto-queue@0.1.0: {} - zod-validation-error@4.0.2(zod@4.6.5): + zod-validation-error@4.0.2(zod@4.6.7): dependencies: - zod: 4.6.5 + zod: 4.6.7 - zod@4.6.5: {} + zod@4.6.7: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3399aed..669d914 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,12 @@ allowBuilds: 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