diff --git a/.dependency-cruiser.cjs b/.dependency-cruiser.cjs index 84d01599727..08daf98fca4 100644 --- a/.dependency-cruiser.cjs +++ b/.dependency-cruiser.cjs @@ -1,6 +1,19 @@ /** @type {import('dependency-cruiser').IConfiguration} */ module.exports = { forbidden: [ + { + name: "no-non-type-@type-exports", + severity: "error", + comment: + "Files in @types should not export anything but types and interfaces. " + + "The folder is intended to house imports that are removed at runtime, " + + "and thus should not contain anything with a bearing on runtime code.", + from: {}, + to: { + path: "(^|/)src/@types", + dependencyTypesNot: ["type-only"], + }, + }, { name: "only-type-imports", severity: "error", @@ -310,7 +323,7 @@ module.exports = { conditionNames: ["import", "require", "node", "default", "types"], /* The extensions, by default are the same as the ones dependency-cruiser - can access (run `npx depcruise --info` to see which ones that are in + can access (run `pnpm exec depcruise --info` to see which ones that are in _your_ environment). If that list is larger than you need you can pass the extensions you actually use (e.g. [".js", ".jsx"]). This can speed up module resolution, which is the most expensive step. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c7d8b1e4d9c..c358986281b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -68,8 +68,8 @@ Do the reviewers need to do something special in order to test your changes? - [ ] The PR is self-contained and cannot be split into smaller PRs? - [ ] Have I provided a clear explanation of the changes? - [ ] Have I tested the changes manually? -- [ ] Are all unit tests still passing? (`npm run test:silent`) - - [ ] Have I created new automated tests (`npm run test:create`) or updated existing tests related to the PR's changes? +- [ ] Are all unit tests still passing? (`pnpm test:silent`) + - [ ] Have I created new automated tests (`pnpm test:create`) or updated existing tests related to the PR's changes? - [ ] Have I provided screenshots/videos of the changes (if applicable)? - [ ] Have I made sure that any UI change works for both UI themes (default and legacy)? diff --git a/.github/workflows/deploy-beta.yml b/.github/workflows/deploy-beta.yml index 90b3008c8e9..44df0310557 100644 --- a/.github/workflows/deploy-beta.yml +++ b/.github/workflows/deploy-beta.yml @@ -18,15 +18,24 @@ jobs: with: submodules: "recursive" ref: ${{ vars.BETA_DEPLOY_BRANCH || 'beta'}} + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" + - name: Install dependencies - run: npm ci + run: pnpm i + - name: Build - run: npm run build:beta + run: pnpm build:beta env: NODE_ENV: production + - name: Set up SSH run: | mkdir ~/.ssh @@ -34,6 +43,7 @@ jobs: echo "${{ secrets.BETA_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/* ssh-keyscan -H ${{ secrets.BETA_SSH_HOST }} >> ~/.ssh/known_hosts + - name: Deploy build on server run: | rsync --del --no-times --checksum -vrm dist/* ${{ secrets.BETA_SSH_USER }}@${{ secrets.BETA_SSH_HOST }}:${{ secrets.BETA_DESTINATION_DIR }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a233a2fccab..0e7102a41dd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,15 +16,24 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' + - name: Install dependencies - run: npm ci + run: pnpm i + - name: Build - run: npm run build + run: pnpm build env: NODE_ENV: production + - name: Set up SSH if: github.event_name == 'push' && github.ref_name == 'main' run: | @@ -33,11 +42,13 @@ jobs: echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/* ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts + - name: Deploy build on server if: github.event_name == 'push' && github.ref_name == 'main' run: | rsync --del --no-times --checksum -vrm dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_DIR }} ssh -t ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "~/prmanifest --inpath ${{ secrets.DESTINATION_DIR }} --outpath ${{ secrets.DESTINATION_DIR }}/manifest.json" + - name: Purge Cloudflare Cache if: github.event_name == 'push' && github.ref_name == 'main' id: purge-cache diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index ce7c17e2db9..fff90047df2 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -34,6 +34,11 @@ jobs: sudo apt update sudo apt install -y git openssh-client + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup Node 22.14.1 uses: actions/setup-node@v4 with: @@ -50,13 +55,13 @@ jobs: working-directory: ${{env.api-dir}} run: | cd pokerogue_docs - npm ci + pnpm i - name: Generate Typedoc docs working-directory: ${{env.api-dir}} run: | cd pokerogue_docs - npm run docs -- --out /tmp/docs --githubPages false --entryPoints ./src/ + pnpm exec typedoc --out /tmp/docs --githubPages false --entryPoints ./src/ - name: Commit & Push docs if: github.event_name == 'push' diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 0fdbc8b1952..0b2e4c1a5da 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -23,20 +23,22 @@ jobs: with: submodules: 'recursive' + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' - cache: 'npm' + cache: 'pnpm' - name: Install Node.js dependencies - run: npm ci - - - name: Run ESLint - run: npm run eslint-ci + run: pnpm i - name: Lint with Biome - run: npm run biome-ci + run: pnpm biome-ci - name: Check dependencies with depcruise - run: npm run depcruise \ No newline at end of file + run: pnpm depcruise \ No newline at end of file diff --git a/.github/workflows/test-shard-template.yml b/.github/workflows/test-shard-template.yml index 98836bd335a..b154166f81b 100644 --- a/.github/workflows/test-shard-template.yml +++ b/.github/workflows/test-shard-template.yml @@ -28,12 +28,20 @@ jobs: uses: actions/checkout@v4.2.2 with: submodules: "recursive" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - cache: "npm" + cache: "pnpm" + - name: Install Node.js dependencies - run: npm ci + run: pnpm i + - name: Run tests - run: npx vitest --project ${{ inputs.project }} --no-isolate --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }} + run: pnpm exec vitest --project ${{ inputs.project }} --no-isolate --shard=${{ inputs.shard }}/${{ inputs.totalShards }} ${{ !runner.debug && '--silent' || '' }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c3b9666caa9..d3dd23eb379 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,16 +1,14 @@ name: Tests on: - # Trigger the workflow on push or pull request, - # but only for the main branch push: branches: - - main # Trigger on push events to the main branch - - beta # Trigger on push events to the beta branch + - main + - beta pull_request: branches: - - main # Trigger on pull request events targeting the main branch - - beta # Trigger on pull request events targeting the beta branch + - main + - beta merge_group: types: [checks_requested] @@ -24,6 +22,7 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 id: filter with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..5f4ee992cb3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,164 @@ +# Contributing to PokéRogue + +Thank you for taking the time to contribute, every little bit helps. This project is entirely open-source and unmonetized - community contributions are what keep it alive! + +Please make sure you understand everything relevant to your changes from the [Table of Contents](#-table-of-contents), and absolutely *feel free to reach out reach out in the **#dev-corner** channel on [Discord](https://discord.gg/pokerogue)*. We are here to help and the better you understand what you're working on, the easier it will be for it to find its way into the game. + +## 📄 Table of Contents + +- [Development Basics](#️-development-basics) +- [Environment Setup](#-environment-setup) +- [Getting Started](#-getting-started) +- [Documentation](#-documentation) +- [Testing Your Changes](#-testing-your-changes) +- [Localization](#-localization) +- [Development Save File (Unlock Everything)](#-development-save-file) + +## 🛠️ Development Basics + +PokéRogue is built with [Typescript](https://www.typescriptlang.org/docs/handbook/intro.html), using the [Phaser](https://github.com/phaserjs/phaser) game framework. + +If you have the motivation and experience with Typescript/Javascript (or are willing to learn) you can contribute by forking the repository and making pull requests with contributions. + +## 💻 Environment Setup + +### Prerequisites + +- node: >=22.14.0 - [manage with pnpm](https://pnpm.io/cli/env) | [manage with fnm](https://github.com/Schniz/fnm) | [manage with nvm](https://github.com/nvm-sh/nvm) +- pnpm: 10.x - [how to install](https://pnpm.io/installation) (not recommended to install via `npm` on Windows native) | [alternate method - volta.sh](https://volta.sh/) + +### Running Locally + +1. Clone the repo and in the root directory run `pnpm install` + - *if you run into any errors, reach out in the **#dev-corner** channel on Discord* +2. Run `pnpm start:dev` to locally run the project at `localhost:8000` + +### Linting + +Check out our [in-depth file](./docs/linting.md) on linting and formatting! + +## 🚀 Getting Started + +A great way to develop an understanding of how the project works is to look at test cases (located in [the `test` folder](./test/)). +Tests show you both how things are supposed to work and the expected "flow" to get from point A to point B in battles. + +*This is a big project and you will be confused at times - never be afraid to reach out and ask questions in **#dev-corner***! + +### Where to Look + +Once you have your feet under you, check out the [Issues](https://github.com/pagefaultgames/pokerogue/issues) page to see how you can help us! +Most issues are bugs and are labeled with their area, such as `Move`, `Ability`, `UI/UX`, etc. There are also priority labels: +- `P0`: Completely gamebreaking (very rare) +- `P1`: Major - Game crash +- `P2`: Minor - Incorrect (but non-crashing) move/ability/interaction +- `P3`: No gameplay impact - typo, minor graphical error, etc. + +Also under issues, you can take a look at the [List of Partial / Unimplemented Moves and Abilities](https://github.com/pagefaultgames/pokerogue/issues/3503) and the [Bug Board](https://github.com/orgs/pagefaultgames/projects/3) (the latter is essentially the same as the issues page but easier to work with). + +You are free to comment on any issue so that you may be assigned to it and we can avoid multiple people working on the same thing. + +## 📚 Documentation + +You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html). +For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file. +For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file. + +Again, if you have unanswered questions please feel free to ask! + +## 🧪 Testing Your Changes + +You've just made a change - how can you check if it works? You have two areas to hit: + +### 1 - Manual Testing + +> This will likely be your first stop. After making a change, you'll want to spin the game up and make sure everything is as you expect. To do this, you will need a way to manipulate the game to produce the situation you're looking to test. + +[src/overrides.ts](../src/overrides.ts) contains overrides for most values you'll need to change for testing, controlled through the `overrides` object. +For example, here is how you could test a scenario where the player Pokemon has the ability Drought and the enemy Pokemon has the move Water Gun: + +```typescript +const overrides = { + ABILITY_OVERRIDE: AbilityId.DROUGHT, + OPP_MOVESET_OVERRIDE: MoveId.WATER_GUN, +} satisfies Partial>; +``` + +Read through `src/overrides.ts` file to find the override that fits your needs - there are a lot of them! +If the situation you're trying to test can't be created using existing overrides (or with the [Dev Save](#-development-save-file)), reach out in **#dev-corner**. +You can get help testing your specific changes, and you might have found a new override that needs to be created! + +### 2 - Automatic Testing + +> PokéRogue uses [Vitest](https://vitest.dev/) for automatic testing. Checking out the existing tests in the [test](./test/) folder is a great way to understand how this works, and to get familiar with the project as a whole. + +To make sure your changes didn't break any existing test cases, run `pnpm test:silent` in your terminal. You can also provide an argument to the command: to run only the Dancer (ability) tests, you could write `pnpm test:silent dancer`. + - __Note that passing all test cases does *not* guarantee that everything is working properly__. The project does not have complete regression testing. + +Most non-trivial changes (*especially bug fixes*) should come along with new test cases. + - To make a new test file, run `pnpm test:create` and follow the prompts. If the move/ability/etc. you're modifying already has tests, simply add new cases to the end of the file. As mentioned before, the easiest way to get familiar with the system and understand how to write your own tests is simply to read the existing tests, particularly ones similar to the tests you intend to write. + - Ensure that new tests: + - Are deterministic. In other words, the test should never pass or fail when it shouldn't due to randomness. This involves primarily ensuring that abilities and moves are never randomly selected. + - As much as possible, are unit tests. If you have made two distinct changes, they should be tested in two separate cases. + - Test edge cases. A good strategy is to think of edge cases beforehand and create tests for them using `it.todo`. Once the edge case has been handled, you can remove the `todo` marker. + +## 📜 Localization + +The project intends for all text to be localized. That is, strings are pulled from translation files using keys (depending on the current language) and *never* hardcoded as a particular language. Note that there is a PDF in a message pinned in **#dev-corner** which gives the following information in greater detail. + +### Setting Up and Updating the Locales Submodule +> The locales (translation) files are set up as a git submodule. A project-in-a-project, if you will. + +To fetch translations when you first start development in your fork or to update them on your local branch, run: +```bash +git submodule update --progress --init --recursive +``` + +### How Localizations Work +> This project uses the [i18next](https://www.i18next.com/) library to integrate translations from public/locales +into the source code based on the user's settings or location. The basic process for +fetching translated text is as follows: +1. The source code fetches text by a given key, e.g. + + ```ts + i18next.t("fileName:keyName", { arg1: "Hello", arg2: "an example", ... }) + ``` +2. The game looks up the key in the corresponding JSON file in the user's +language, e.g. + + ```ts + // from "en/file-name.json"... + "keyName": "{{arg1}}! This is {{arg2}} of translated text!" + ``` + If the key doesn't exist for the user's language, the game will default to the +corresponding English key (in the case of LATAM Spanish, it will first default to ES Spanish). + +3. The game shows the text to the user: + + ```ts + "Hello! This is an example of translated text!" + ``` +### Adding Translated Text +> If your feature involves new or modified text in any form, then you will be modifying the [locales](https://github.com/pagefaultgames/pokerogue-locales) repository. ***Never hardcode new text in any language!*** + +The workflow is: + +1. Make a pull request to the main repository for your new feature. +If this feature requires new text, the text should be integrated into the code with a new i18next key pointing to where you plan to add it into the pokerogue-locales repository. + +2. Make another pull request -- this time to the [pokerogue-locales](https://github.com/pagefaultgames/pokerogue-locales) +repository -- adding a new entry to the English locale with text for each key +you added to your main PR. You *only* need to add the English key and value - the translation team will handle the rest. + +3. If your feature is pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), add a source link for any added text within the locale PR. +[Poké Corpus](https://abcboy101.github.io/poke-corpus) is a great resource for finding text from the latest mainline games; otherwise, a YouTube video link showing the text in mainline is sufficient. + +4. Ping @lugiadrien in **#dev-corner** or the current callout thread to make sure your locales PR is seen. +It'll be merged into the locales repository after any necessary corrections, at which point you can test it in your main PR (after updating locales from remote) + +5. The Dev team will approve your main PR, and your changes will be in the beta environment! + +## 😈 Development Save File +> Some issues may require you to have unlocks on your save file which go beyond normal overrides. For this reason, the repository contains a [save file](../test/testUtils/saves/everything.psrv) with _everything_ unlocked (even ones not legitimately obtainable, like unimplemented variant shinies). + +1. Start the game up locally and navigate to `Menu -> Manage Data -> Import Data` +2. Select [everything.prsv](test/testUtils/saves/everything.prsv) (`test/testUtils/saves/everything.prsv`) and confirm. diff --git a/README.md b/README.md index 56392808b3c..1bb8c7772f3 100644 --- a/README.md +++ b/README.md @@ -4,47 +4,7 @@ PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite # Contributing -## 🛠️ Development - -If you have the motivation and experience with Typescript/Javascript (or are willing to learn) please feel free to fork the repository and make pull requests with contributions. If you don't know what to work on but want to help, reference the below **To-Do** section or the **#feature-vote** channel in the discord. - -### 💻 Environment Setup - -#### Prerequisites - -- node: 22.14.0 -- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - -#### Running Locally - -1. Clone the repo and in the root directory run `npm install` - - *if you run into any errors, reach out in the **#dev-corner** channel in discord* -2. Run `npm run start:dev` to locally run the project in `localhost:8000` - -#### Linting - -We're using Biome as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run biome` script. To view the complete rules, check out the [biome.jsonc](./biome.jsonc) file. - -### 📚 Documentation - -You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html). -For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file. -For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file. - -### ❔ FAQ - -**How do I test a new _______?** - -- In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing - -**How do I retrieve the translations?** - -- The translations were moved to the [dedicated translation repository](https://github.com/pagefaultgames/pokerogue-locales) and are now applied as a submodule in this project. -- The command to retrieve the translations is `git submodule update --init --recursive`. If you still struggle to get it working, please reach out to #dev-corner channel in Discord. - -## 🪧 To Do - -Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to see how can you help us! +See [CONTRIBUTING.md](./CONTRIBUTING.md), this includes instructions on how to set up the game locally. # 📝 Credits > diff --git a/biome.jsonc b/biome.jsonc index d183334ad58..e517d3a18d0 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json", "vcs": { "enabled": false, "clientKind": "git", @@ -10,35 +10,47 @@ "enabled": true, "useEditorconfig": true, "indentStyle": "space", - "ignore": ["src/enums/*", "src/data/balance/*"], + "includes": ["**", "!**/src/enums/**/*", "!**/src/data/balance/**/*"], "lineWidth": 120 }, "files": { "ignoreUnknown": true, // Adding folders to the ignore list is GREAT for performance because it prevents biome from descending into them // and having to verify whether each individual file is ignored - "ignore": [ - "**/*.d.ts", - "dist/*", - "build/*", - "coverage/*", - "public/*", - ".github/*", - "node_modules/*", - ".vscode/*", - "*.css", // TODO? - "*.html", // TODO? - // TODO: these files are too big and complex, ignore them until their respective refactors - "src/data/moves/move.ts", - - // this file is just too big: - "src/data/balance/tms.ts" + "includes": [ + "**", + "!**/*.d.ts", + "!**/dist/**/*", + "!**/build/**/*", + "!**/coverage/**/*", + "!**/public/**/*", + "!**/.github/**/*", + "!**/node_modules/**/*", + "!**/.vscode/**/*", + "!**/typedoc/**/*", + // TODO: lint css and html? + "!**/*.css", + "!**/*.html", + // TODO: enable linting this file + "!**/src/data/moves/move.ts", + // this file is too big + "!**/src/data/balance/tms.ts" ] }, - // While it'd be nice to enable consistent sorting, enabling this causes issues due to circular import resolution order - // TODO: Remove if we ever get down to 0 circular imports - "organizeImports": { "enabled": false }, + // TODO: Configure and enable import sorting + "assist": { + "actions": { + "source": { + "organizeImports": { + "level": "off", + "options": { + "groups": [] + } + } + } + } + }, "linter": { "enabled": true, "rules": { @@ -48,10 +60,15 @@ "noUnusedVariables": "error", "noSwitchDeclarations": "error", "noVoidTypeReturn": "error", - "noUnusedImports": "error" + "noUnusedImports": { + "level": "error", + "fix": "safe" + }, + "noUnusedFunctionParameters": "error", + "noUnusedLabels": "error", + "noPrivateImports": "error" }, "style": { - "noVar": "error", "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome "useBlockStatements": "error", "useConst": "error", @@ -59,11 +76,31 @@ "noNonNullAssertion": "off", // TODO: Turn this on ASAP and fix all non-null assertions in non-test files "noParameterAssign": "off", "useExponentiationOperator": "off", // Too typo-prone and easy to mixup with standard multiplication (* vs **) - "useDefaultParameterLast": "off", // TODO: Fix spots in the codebase where this flag would be triggered, and then enable + "useDefaultParameterLast": { + // TODO: Fix spots in the codebase where this flag would be triggered + // and then set to "error" and re-enable the fixer + "level": "warn", + "fix": "none" + }, "useSingleVarDeclarator": "off", "useNodejsImportProtocol": "off", "useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation - "noNamespaceImport": "error" + "useAsConstAssertion": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error", + "noRestrictedTypes": { + "level": "error", + "options": { + "types": { + "integer": { + "message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", + "use": "number" + } + } + } + } }, "suspicious": { "noDoubleEquals": "error", @@ -77,45 +114,62 @@ "noImplicitAnyLet": "warn", // TODO: Refactor and make this an error "noRedeclare": "info", // TODO: Refactor and make this an error "noGlobalIsNan": "off", - "noAsyncPromiseExecutor": "warn" // TODO: Refactor and make this an error + "noAsyncPromiseExecutor": "warn", // TODO: Refactor and make this an error + "noVar": "error", + "noDocumentCookie": "off" // Firefox has minimal support for the "Cookie Store API" }, "complexity": { - "noExcessiveCognitiveComplexity": "warn", // TODO: Refactor and make this an error + "noExcessiveCognitiveComplexity": "info", // TODO: Refactor and make this an error "useLiteralKeys": "off", "noForEach": "off", // Foreach vs for of is not that simple. "noUselessSwitchCase": "off", // Explicit > Implicit "noUselessConstructor": "error", - "noBannedTypes": "warn" // TODO: Refactor and make this an error + "noBannedTypes": "warn", // TODO: Refactor and make this an error + "noThisInStatic": "error", + "noUselessThisAlias": "error", + "noUselessTernary": "error" + }, + "performance": { + "noNamespaceImport": "error", + "noDelete": "error" }, "nursery": { - "noRestrictedTypes": { - "level": "error", - "options": { - "types": { - "integer": { - "message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", - "use": "number" - } - } - } - } + "useAdjacentGetterSetter": "error", + "noConstantBinaryExpression": "error", + "noTsIgnore": "error", + "noAwaitInLoop": "warn", + "useJsonImportAttribute": "off", // "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'nodenext', or 'preserve'. ts(2823)" + "useIndexOf": "error", + "useObjectSpread": "error", + "useNumericSeparators": "off", // TODO: enable? + "useIterableCallbackReturn": "warn", // TODO: refactor and make "error" + "noShadow": "warn" // TODO: refactor and make "error" } } }, "javascript": { - "formatter": { "quoteStyle": "double", "arrowParentheses": "asNeeded" } + "formatter": { + "quoteStyle": "double", + "arrowParentheses": "asNeeded" + }, + "parser": { + "jsxEverywhere": false + } }, "overrides": [ { - "include": ["test/**/*.test.ts"], - "javascript": { "globals": [] }, + "includes": ["**/test/**/*.test.ts"], "linter": { "rules": { "performance": { - "noDelete": "off" // TODO: evaluate if this is necessary for the test(s) to function + "noDelete": "off", // TODO: evaluate if this is necessary for the test(s) to function + "noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests }, "style": { - "noNamespaceImport": "off" // this is required for `vi.spyOn` to work in some tests + "noNonNullAssertion": "off" + }, + "nursery": { + "noFloatingPromises": "error" } } } @@ -123,7 +177,7 @@ // Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes) { - "include": ["src/overrides.ts", "src/enums/*"], + "includes": ["**/src/overrides.ts", "**/src/enums/**/*"], "linter": { "rules": { "correctness": { @@ -133,7 +187,7 @@ } }, { - "include": ["src/overrides.ts"], + "includes": ["**/src/overrides.ts"], "linter": { "rules": { "style": { diff --git a/docs/comments.md b/docs/comments.md index ba6c9929625..ac9474ce5b2 100644 --- a/docs/comments.md +++ b/docs/comments.md @@ -23,7 +23,7 @@ When formatted correctly, these comments are shown within VS Code or similar IDE - Functions also show the comment for each parameter as you type them, making keeping track of arguments inside lengthy functions much more clear. They can also be used to generate a commentated overview of the codebase. There is a GitHub action that automatically updates [this docs site](https://pagefaultgames.github.io/pokerogue/main/index.html) -and you can generate it locally as well via `npm run docs` which will generate into the `typedoc/` directory. +and you can generate it locally as well via `pnpm run docs` which will generate into the `typedoc/` directory. ## Syntax For an example of how TSDoc comments work, here are some TSDoc comments taken from `src/data/moves/move.ts`: diff --git a/docs/linting.md b/docs/linting.md index d3b4e47675f..d925b2f29af 100644 --- a/docs/linting.md +++ b/docs/linting.md @@ -1,14 +1,10 @@ # Linting & Formatting -> "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." -> -> — Martin Fowler - Writing clean, readable code is important, and linters and formatters are an integral part of ensuring code quality and readability. It is for this reason we are using [Biome](https://biomejs.dev), an opinionated linter/formatter (akin to Prettier) with a heavy focus on speed and performance. ### Installation -You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `npm install` after cloning the repo (assuming you followed proper instructions, that is). If you haven't done that yet, go do it. +You probably installed Biome already without noticing it - it's included inside `package.json` and should've been downloaded when you ran `pnpm install` after cloning the repo. If you haven't done that yet, go do it. # Using Biome @@ -24,17 +20,11 @@ You will **not** be able to push code with `error`-level linting problems - fix We also have a [Github Action](../.github/workflows/quality.yml) to verify code quality each time a PR is updated, preventing bad code from inadvertently making its way upstream. -### Why am I getting errors for code I didn't write? - -To save time and minimize friction with existing code, both the pre-commit hook and workflow run will only check files **directly changed** by a given PR or commit. -As a result, changes to files not updated since Biome's introduction can cause any _prior_ linting errors in them to resurface and get flagged. -This should occur less and less often as time passes and more files are updated to the new standard. - ## Running Biome via CLI If you want Biome to check your files manually, you can run it from the command line like so: ```sh -npx biome check --[flags] +pnpm exec biome check --[flags] ``` A full list of flags and options can be found on [their website](https://biomejs.dev/reference/cli/), but here's a few useful ones to keep in mind: @@ -56,10 +46,3 @@ Some things to consider: Any questions about linting rules should be brought up in the `#dev-corner` channel in the discord. [^1]: A complete list of rules can be found in the `biome.jsonc` file in the project root. - -## What about ESLint? - - -Our project migrated away from ESLint around March 2025 due to it simply not scaling well enough with the codebase's ever-growing size. The [existing eslint rules](../eslint.config.js) are considered _deprecated_, only kept due to Biome lacking the corresponding rules in its current ruleset. - -No additional ESLint rules should be added under any circumstances - even the few currently in circulation take longer to run than the entire Biome formatting/linting suite combined. \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index aebcab7feae..00000000000 --- a/eslint.config.js +++ /dev/null @@ -1,43 +0,0 @@ -/** @ts-check */ -import tseslint from "typescript-eslint"; -import stylisticTs from "@stylistic/eslint-plugin-ts"; -import parser from "@typescript-eslint/parser"; -import importX from "eslint-plugin-import-x"; - -export default tseslint.config( - { - name: "eslint-config", - files: ["src/**/*.{ts,tsx,js,jsx}", "test/**/*.{ts,tsx,js,jsx}"], - ignores: ["dist/*", "build/*", "coverage/*", "public/*", ".github/*", "node_modules/*", ".vscode/*"], - languageOptions: { - parser: parser, - }, - plugins: { - "import-x": importX, - "@stylistic/ts": stylisticTs, - "@typescript-eslint": tseslint.plugin, - }, - rules: { - "no-undef": "off", // Disables the rule that disallows the use of undeclared variables (TypeScript handles this) - "no-extra-semi": "error", // Disallows unnecessary semicolons for TypeScript-specific syntax - "import-x/extensions": ["error", "never", { json: "always" }], // Enforces no extension for imports unless json - }, - }, - { - name: "eslint-tests", - files: ["test/**/**.test.ts"], - languageOptions: { - parser: parser, - parserOptions: { - project: ["./tsconfig.json"], - }, - }, - plugins: { - "@typescript-eslint": tseslint.plugin, - }, - rules: { - "@typescript-eslint/no-floating-promises": "error", // Require Promise-like statements to be handled appropriately. - https://typescript-eslint.io/rules/no-floating-promises/ - "@typescript-eslint/no-misused-promises": "error", // Disallow Promises in places not designed to handle them. - https://typescript-eslint.io/rules/no-misused-promises/ - }, - }, -); diff --git a/lefthook.yml b/lefthook.yml index 0f91f658171..28178432c31 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,8 +1,7 @@ pre-commit: - parallel: true commands: biome-lint: - run: npx biome check --write --reporter=summary --staged --no-errors-on-unmatched + run: pnpm exec biome check --write --reporter=summary --staged --no-errors-on-unmatched stage_fixed: true skip: - merge diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 5ee523f965b..00000000000 --- a/package-lock.json +++ /dev/null @@ -1,8029 +0,0 @@ -{ - "name": "pokemon-rogue-battle", - "version": "1.9.6", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "pokemon-rogue-battle", - "version": "1.9.6", - "hasInstallScript": true, - "dependencies": { - "@material/material-color-utilities": "^0.2.7", - "compare-versions": "^6.1.1", - "crypto-js": "^4.2.0", - "i18next": "^24.2.2", - "i18next-browser-languagedetector": "^8.0.4", - "i18next-http-backend": "^3.0.2", - "i18next-korean-postposition-processor": "^1.0.0", - "json-stable-stringify": "^1.2.0", - "jszip": "^3.10.1", - "phaser": "^3.88.2", - "phaser3-rex-plugins": "^1.80.15" - }, - "devDependencies": { - "@biomejs/biome": "1.9.4", - "@eslint/js": "^9.23.0", - "@hpcc-js/wasm": "^2.22.4", - "@stylistic/eslint-plugin-ts": "^4.1.0", - "@types/jsdom": "^21.1.7", - "@types/node": "^22.13.14", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", - "@vitest/coverage-istanbul": "^3.0.9", - "dependency-cruiser": "^16.3.10", - "eslint": "^9.23.0", - "eslint-plugin-import-x": "^4.9.4", - "inquirer": "^12.4.2", - "jsdom": "^26.0.0", - "lefthook": "^1.11.5", - "msw": "^2.7.3", - "phaser3spectorjs": "^0.0.8", - "rollup": "^4.40.1", - "typedoc": "^0.28.1", - "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", - "vite": "^6.3.4", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.0.9", - "vitest-canvas-mock": "^0.3.3" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", - "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.2", - "@csstools/css-color-parser": "^3.0.8", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", - "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", - "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", - "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", - "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cookie": "^0.7.2" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", - "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", - "dev": true, - "license": "ISC", - "dependencies": { - "statuses": "^2.0.1" - } - }, - "node_modules/@bundled-es-modules/tough-cookie": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", - "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@types/tough-cookie": "^4.0.5", - "tough-cookie": "^4.1.4" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", - "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", - "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz", - "integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", - "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", - "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.2.2.tgz", - "integrity": "sha512-vaZNGhGLKMY14HbF53xxHNgFO9Wz+t5lTlGNpl2N9xFiKQ0I5oIe0vKjU9dh7Nb3Dw6lZ7wqUE0ri+zcdpnK+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-oniguruma": "^3.2.1", - "@shikijs/langs": "^3.2.1", - "@shikijs/themes": "^3.2.1", - "@shikijs/types": "^3.2.1", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@hpcc-js/wasm": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.22.4.tgz", - "integrity": "sha512-58JkRkxZffiBAbZhc7z+9iaaAOmn1cyxLL3rRwsUvco/I0Wwb7uVAlHM9HiU6XASe2k11jrIjCFff1t9QKjlqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "yargs": "17.7.2" - }, - "bin": { - "dot-wasm": "node ./node_modules/@hpcc-js/wasm-graphviz-cli/bin/index.js" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.4.tgz", - "integrity": "sha512-d30576EZdApjAMceijXA5jDzRQHT/MygbC+J8I7EqA6f/FRpYxlRtRJbHF8gHeWYeSdOuTEJqonn7QLB1ELezA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.8.tgz", - "integrity": "sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz", - "integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/editor": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.9.tgz", - "integrity": "sha512-8HjOppAxO7O4wV1ETUlJFg6NDjp/W2NP5FB9ZPAcinAlNT4ZIWOLe2pUVwmmPRSV0NMdI5r/+lflN55AwZOKSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "external-editor": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.11.tgz", - "integrity": "sha512-OZSUW4hFMW2TYvX/Sv+NnOZgO8CHT2TU1roUCUIF2T+wfw60XFRRp9MRUPCT06cRnKL+aemt2YmTWwt7rOrNEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", - "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.8.tgz", - "integrity": "sha512-WXJI16oOZ3/LiENCAxe8joniNp8MQxF6Wi5V+EBbVA0ZIOpFcL4I9e7f7cXse0HJeIPCWO8Lcgnk98juItCi7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.11.tgz", - "integrity": "sha512-pQK68CsKOgwvU2eA53AG/4npRTH2pvs/pZ2bFvzpBhrznh8Mcwt19c+nMO7LHRr3Vreu1KPhNBF3vQAKrjIulw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.11.tgz", - "integrity": "sha512-dH6zLdv+HEv1nBs96Case6eppkRggMe8LoOTl30+Gq5Wf27AO/vHFgStTVz4aoevLdNXqwE23++IXGw4eiOXTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.0.tgz", - "integrity": "sha512-EZiJidQOT4O5PYtqnu1JbF0clv36oW2CviR66c7ma4LsupmmQlUwmdReGKRp456OWPWMz3PdrPiYg3aCk3op2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.1.4", - "@inquirer/confirm": "^5.1.8", - "@inquirer/editor": "^4.2.9", - "@inquirer/expand": "^4.0.11", - "@inquirer/input": "^4.1.8", - "@inquirer/number": "^3.0.11", - "@inquirer/password": "^4.0.11", - "@inquirer/rawlist": "^4.0.11", - "@inquirer/search": "^3.0.11", - "@inquirer/select": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.11.tgz", - "integrity": "sha512-uAYtTx0IF/PqUAvsRrF3xvnxJV516wmR6YVONOmCWJbbt87HcDHLfL9wmBQFbNJRv5kCjdYKrZcavDkH3sVJPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.11.tgz", - "integrity": "sha512-9CWQT0ikYcg6Ls3TOa7jljsD7PgjcsYEM0bYE+Gkz+uoW9u8eaJCRHJKkucpRE5+xKtaaDbrND+nPDoxzjYyew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.0.tgz", - "integrity": "sha512-z0a2fmgTSRN+YBuiK1ROfJ2Nvrpij5lVN3gPDkQGhavdvIVGHGW29LwYZfM/j42Ai2hUghTI/uoBuTbrJk42bA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", - "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@material/material-color-utilities": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz", - "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==", - "license": "Apache-2.0" - }, - "node_modules/@mswjs/interceptors": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.6.tgz", - "integrity": "sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz", - "integrity": "sha512-5yximcFK5FNompXfJFoWanu5l8v1hNGqNHh9du1xETp9HWk/B/PzvchX55WYOPaIeNglG8++68AAiauBAtbnzw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.3.1", - "@emnapi/runtime": "^1.3.1", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.0.tgz", - "integrity": "sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", - "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", - "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", - "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", - "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", - "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", - "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", - "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", - "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", - "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", - "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", - "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", - "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", - "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", - "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", - "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", - "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", - "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", - "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", - "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", - "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.2.1.tgz", - "integrity": "sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.2.1.tgz", - "integrity": "sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.2.1.tgz", - "integrity": "sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.2.1" - } - }, - "node_modules/@shikijs/types": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.2.1.tgz", - "integrity": "sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@stylistic/eslint-plugin-ts": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-4.2.0.tgz", - "integrity": "sha512-j2o2GvOx9v66x8hmp/HJ+0T+nOppiO5ycGsCkifh7JPGgjxEhpkGmIGx3RWsoxpWbad3VCX8e8/T8n3+7ze1Zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^8.23.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=9.0.0" - } - }, - "node_modules/@stylistic/eslint-plugin-ts/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/doctrine": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", - "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/jsdom": { - "version": "21.1.7", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", - "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.13.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", - "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/statuses": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz", - "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/type-utils": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", - "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", - "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", - "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", - "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", - "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz", - "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", - "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.29.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.3.3.tgz", - "integrity": "sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.3.3.tgz", - "integrity": "sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.3.3.tgz", - "integrity": "sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.3.3.tgz", - "integrity": "sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.3.3.tgz", - "integrity": "sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.3.3.tgz", - "integrity": "sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.3.3.tgz", - "integrity": "sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.3.3.tgz", - "integrity": "sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.3.3.tgz", - "integrity": "sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.3.3.tgz", - "integrity": "sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.3.3.tgz", - "integrity": "sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.7" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.3.3.tgz", - "integrity": "sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.3.3.tgz", - "integrity": "sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.3.3.tgz", - "integrity": "sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@vitest/coverage-istanbul": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-3.0.9.tgz", - "integrity": "sha512-/TXh2qmOhclmVPjOnPTpIO4Xr6l2P5EwyXQygenwq4/ZQ/vPsrz+GCRZF9kBeQi6xrGcHv368Si9PGImWQawVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@istanbuljs/schema": "^0.1.3", - "debug": "^4.4.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-instrument": "^6.0.3", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magicast": "^0.3.5", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "3.0.9" - } - }, - "node_modules/@vitest/expect": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", - "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.0.9", - "@vitest/utils": "3.0.9", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", - "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.0.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", - "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", - "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.0.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", - "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.0.9", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", - "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", - "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.0.9", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-jsx-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/acorn-jsx-walk/-/acorn-jsx-walk-2.0.0.tgz", - "integrity": "sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn-loose": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.4.0.tgz", - "integrity": "sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/compare-versions": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/cssfontparser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", - "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", - "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.1.1", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/dagre": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", - "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", - "license": "MIT", - "dependencies": { - "graphlib": "^2.1.8", - "lodash": "^4.17.15" - } - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dependency-cruiser": { - "version": "16.3.10", - "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-16.3.10.tgz", - "integrity": "sha512-WkCnibHBfvaiaQ+S46LZ6h4AR6oj42Vsf5/0Vgtrwdwn7ZekMJdZ/ALoTwNp/RaGlKW+MbV/fhSZOvmhAWVWzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "8.12.1", - "acorn-jsx": "5.3.2", - "acorn-jsx-walk": "2.0.0", - "acorn-loose": "8.4.0", - "acorn-walk": "8.3.3", - "ajv": "8.17.1", - "commander": "12.1.0", - "enhanced-resolve": "5.17.1", - "ignore": "5.3.1", - "interpret": "^3.1.1", - "is-installed-globally": "1.0.0", - "json5": "2.2.3", - "memoize": "10.0.0", - "picocolors": "1.0.1", - "picomatch": "4.0.2", - "prompts": "2.4.2", - "rechoir": "^0.8.0", - "safe-regex": "2.1.1", - "semver": "^7.6.3", - "teamcity-service-messages": "0.1.14", - "tsconfig-paths-webpack-plugin": "4.1.0", - "watskeburt": "4.1.0" - }, - "bin": { - "depcruise": "bin/dependency-cruise.mjs", - "depcruise-baseline": "bin/depcruise-baseline.mjs", - "depcruise-fmt": "bin/depcruise-fmt.mjs", - "depcruise-wrap-stream-in-html": "bin/wrap-stream-in-html.mjs", - "dependency-cruise": "bin/dependency-cruise.mjs", - "dependency-cruiser": "bin/dependency-cruise.mjs" - }, - "engines": { - "node": "^18.17||>=20" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.830", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.830.tgz", - "integrity": "sha512-TrPKKH20HeN0J1LHzsYLs2qwXrp8TF4nHdu4sq61ozGbzMpWhI7iIOPYPPkxeq1azMT9PZ8enPFcftbs/Npcjg==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import-x": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.10.0.tgz", - "integrity": "sha512-5ej+0WILhX3D6wkcdsyYmPp10SUIK6fmuZ6KS8nf9MD8CJ6/S/3Dl7m21g+MLeaTMsvcEXo3JunNAbgHwXxs/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.0", - "@types/doctrine": "^0.0.9", - "@typescript-eslint/utils": "^8.28.0", - "debug": "^4.4.0", - "doctrine": "^3.0.0", - "eslint-import-resolver-node": "^0.3.9", - "get-tsconfig": "^4.10.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.3 || ^10.0.1", - "semver": "^7.7.1", - "stable-hash": "^0.0.5", - "tslib": "^2.8.1", - "unrs-resolver": "^1.3.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/graphology": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", - "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", - "license": "MIT", - "dependencies": { - "events": "^3.3.0", - "obliterator": "^2.0.2" - }, - "peerDependencies": { - "graphology-types": ">=0.24.0" - } - }, - "node_modules/graphology-types": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", - "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", - "license": "MIT", - "peer": true - }, - "node_modules/graphql": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", - "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/i18next": { - "version": "24.2.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", - "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.10" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/i18next-browser-languagedetector": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.4.tgz", - "integrity": "sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/i18next-http-backend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", - "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", - "license": "MIT", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, - "node_modules/i18next-korean-postposition-processor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/i18next-korean-postposition-processor/-/i18next-korean-postposition-processor-1.0.0.tgz", - "integrity": "sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==", - "license": "MIT", - "peerDependencies": { - "i18next": ">=8.4.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/inquirer": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.5.0.tgz", - "integrity": "sha512-aiBBq5aKF1k87MTxXDylLfwpRwToShiHrSv4EmB07EYyLgmnjEz5B3rn0aGw1X3JA/64Ngf2T54oGwc+BCsPIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.9", - "@inquirer/prompts": "^7.4.0", - "@inquirer/type": "^3.0.5", - "ansi-escapes": "^4.3.2", - "mute-stream": "^2.0.0", - "run-async": "^3.0.0", - "rxjs": "^7.8.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", - "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-directory": "^4.0.1", - "is-path-inside": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-installed-globally/node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-canvas-mock": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", - "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssfontparser": "^1.2.1", - "moo-color": "^1.0.2" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.1", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/json-stable-stringify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz", - "integrity": "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "license": "Public Domain", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lefthook": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook/-/lefthook-1.11.6.tgz", - "integrity": "sha512-j0VmMM50WlPDassmgvapRum9po29Tv1BXzBNFpzGkk9E91CEG9jKik/OHyH/r/na+q8qNIUUyPL6QQuTN/UhQQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "lefthook": "bin/index.js" - }, - "optionalDependencies": { - "lefthook-darwin-arm64": "1.11.6", - "lefthook-darwin-x64": "1.11.6", - "lefthook-freebsd-arm64": "1.11.6", - "lefthook-freebsd-x64": "1.11.6", - "lefthook-linux-arm64": "1.11.6", - "lefthook-linux-x64": "1.11.6", - "lefthook-openbsd-arm64": "1.11.6", - "lefthook-openbsd-x64": "1.11.6", - "lefthook-windows-arm64": "1.11.6", - "lefthook-windows-x64": "1.11.6" - } - }, - "node_modules/lefthook-darwin-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-darwin-arm64/-/lefthook-darwin-arm64-1.11.6.tgz", - "integrity": "sha512-gWgdWrKgZgX+bKc6Vs/x7JkO+58lLOpRzpteLx//82D0MKVPlNZwjd4zz4AbIBXtM4Hcj+6gSsOzQ7QDXxjVvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-darwin-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-darwin-x64/-/lefthook-darwin-x64-1.11.6.tgz", - "integrity": "sha512-Ia0TjTKuYcSaDTuoCnbWtpPZ2VEoKzgn33OB90VjNaSVs4ooE0PIdpO+w00x1elqIaf1pbrpq6HgeB26Du8KbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/lefthook-freebsd-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-arm64/-/lefthook-freebsd-arm64-1.11.6.tgz", - "integrity": "sha512-PxIwj+hmjLahyzEmcIfalIBDhgklAQCavwM4sGCgbzDi4/+VQX+4aEs4pQqtd7v3aohmjtO/4n2emzTI8donww==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-freebsd-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-freebsd-x64/-/lefthook-freebsd-x64-1.11.6.tgz", - "integrity": "sha512-3o1lMKxz1VtWaP/o117wgUn3ZOpefMoSf+8LuiTzI3/PDprIuzgyw2nXKlBZAMDpNPHMNnJeQNts9XLMRmkldg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/lefthook-linux-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-linux-arm64/-/lefthook-linux-arm64-1.11.6.tgz", - "integrity": "sha512-nKPFZ5cA9f5tVn0ybDVqcXXlpTHZqo05N4KQRhWTj5Nem+JoD2YzJIlvZhdJhUrldERqj6deDMXChH5T3z4Rrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-linux-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-linux-x64/-/lefthook-linux-x64-1.11.6.tgz", - "integrity": "sha512-naN8dllLCOEeP+wznLnq+oXrs1dvt/iMLkcl+pOPWLqFccPfDiHzr8V8GslaTa+rSFsAnvjR7SJIOi5C29xedA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/lefthook-openbsd-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-arm64/-/lefthook-openbsd-arm64-1.11.6.tgz", - "integrity": "sha512-dPxhJfYQ667T+U3pz1+O3mTRNHzXH/BvPlXSH+oy8uiSry4AtVNRXkVvXPUcpLlrAy6HuFYodsrpCIlWFeYwiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-openbsd-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-openbsd-x64/-/lefthook-openbsd-x64-1.11.6.tgz", - "integrity": "sha512-9D26kcSsjiW4D0AuVDdi+0ZqrsOzRWOpMS/kcUbLfrU99yCvma0rMTqKbbDMkVur/znS7qL53oGahXCXDNA+IQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/lefthook-windows-arm64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-windows-arm64/-/lefthook-windows-arm64-1.11.6.tgz", - "integrity": "sha512-xdCenr4+BFnfBEhiXj6GJp02EPmcwTAGa7NYm6hVTfDwGXw24tuLv7lpnGjgK3kovN6EukgLH1FYkeyDOBEMnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/lefthook-windows-x64": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/lefthook-windows-x64/-/lefthook-windows-x64-1.11.6.tgz", - "integrity": "sha512-Fg2GzLhzeDV/GX8+ydrI0wBOytQWpPkNdngx+a8B/feCDbwjAiFklDG5oV4ytuWrtg1JPEEWLJd6nHefj4wtHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true - }, - "node_modules/memoize": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz", - "integrity": "sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/memoize?sponsor=1" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/moo-color": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz", - "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.1.4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/msw": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.3.tgz", - "integrity": "sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.1", - "@bundled-es-modules/statuses": "^1.0.1", - "@bundled-es-modules/tough-cookie": "^0.1.6", - "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.37.0", - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "picocolors": "^1.1.1", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.26.1", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.8.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/msw/node_modules/type-fest": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", - "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz", - "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==", - "dev": true - }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT" - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/outvariant": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true, - "license": "MIT" - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", - "license": "MIT" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/phaser": { - "version": "3.88.2", - "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.88.2.tgz", - "integrity": "sha512-UBgd2sAFuRJbF2xKaQ5jpMWB8oETncChLnymLGHcrnT53vaqiGrQWbUKUDBawKLm24sghjKo4Bf+/xfv8espZQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1" - } - }, - "node_modules/phaser3-rex-plugins": { - "version": "1.80.15", - "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.80.15.tgz", - "integrity": "sha512-Ur973N1W5st6XEYBcJko8eTcEbdDHMM+m7VqvT3j/EJeJwYyJ3bVb33JJDsFgefk3A2iAz2itP/UY7CzxJOJVA==", - "license": "MIT", - "dependencies": { - "dagre": "^0.8.5", - "eventemitter3": "^3.1.2", - "graphology": "^0.25.4", - "i18next": "^22.5.1", - "i18next-http-backend": "^2.5.2", - "js-yaml": "^4.1.0", - "mustache": "^4.2.0", - "papaparse": "^5.4.1", - "webfontloader": "^1.6.28" - } - }, - "node_modules/phaser3-rex-plugins/node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "license": "MIT" - }, - "node_modules/phaser3-rex-plugins/node_modules/i18next": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.5.1.tgz", - "integrity": "sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.6" - } - }, - "node_modules/phaser3-rex-plugins/node_modules/i18next-http-backend": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.7.3.tgz", - "integrity": "sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==", - "license": "MIT", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, - "node_modules/phaser3spectorjs": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/phaser3spectorjs/-/phaser3spectorjs-0.0.8.tgz", - "integrity": "sha512-0dSO7/aMjEUPrp5EcjRvRRsEf+jXDbmzalPeJ6VtTB2Pn1PeaKc+qlL/DmO3l1Dvc5lkzc+Sil1Ta+Hkyi5cbA==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", - "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.1", - "@rollup/rollup-android-arm64": "4.40.1", - "@rollup/rollup-darwin-arm64": "4.40.1", - "@rollup/rollup-darwin-x64": "4.40.1", - "@rollup/rollup-freebsd-arm64": "4.40.1", - "@rollup/rollup-freebsd-x64": "4.40.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", - "@rollup/rollup-linux-arm-musleabihf": "4.40.1", - "@rollup/rollup-linux-arm64-gnu": "4.40.1", - "@rollup/rollup-linux-arm64-musl": "4.40.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-musl": "4.40.1", - "@rollup/rollup-linux-s390x-gnu": "4.40.1", - "@rollup/rollup-linux-x64-gnu": "4.40.1", - "@rollup/rollup-linux-x64-musl": "4.40.1", - "@rollup/rollup-win32-arm64-msvc": "4.40.1", - "@rollup/rollup-win32-ia32-msvc": "4.40.1", - "@rollup/rollup-win32-x64-msvc": "4.40.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "regexp-tree": "~0.1.1" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/teamcity-service-messages": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/teamcity-service-messages/-/teamcity-service-messages-0.1.14.tgz", - "integrity": "sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "6.1.85", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", - "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.85" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.85", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz", - "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tsconfck": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.1.tgz", - "integrity": "sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedoc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.1.tgz", - "integrity": "sha512-Mn2VPNMaxoe/hlBiLriG4U55oyAa3Xo+8HbtEwV7F5WEOPXqtxzGuMZhJYHaqFJpajeQ6ZDUC2c990NAtTbdgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@gerrit0/mini-shiki": "^3.2.1", - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "yaml": "^2.7.0 " - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18", - "pnpm": ">= 10" - }, - "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" - } - }, - "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz", - "integrity": "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.0", - "@typescript-eslint/parser": "8.29.0", - "@typescript-eslint/utils": "8.29.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unrs-resolver": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz", - "integrity": "sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/JounQin" - }, - "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.3.3", - "@unrs/resolver-binding-darwin-x64": "1.3.3", - "@unrs/resolver-binding-freebsd-x64": "1.3.3", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.3.3", - "@unrs/resolver-binding-linux-arm64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-arm64-musl": "1.3.3", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-s390x-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-gnu": "1.3.3", - "@unrs/resolver-binding-linux-x64-musl": "1.3.3", - "@unrs/resolver-binding-wasm32-wasi": "1.3.3", - "@unrs/resolver-binding-win32-arm64-msvc": "1.3.3", - "@unrs/resolver-binding-win32-ia32-msvc": "1.3.3", - "@unrs/resolver-binding-win32-x64-msvc": "1.3.3" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/vite": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", - "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", - "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", - "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "3.0.9", - "@vitest/mocker": "3.0.9", - "@vitest/pretty-format": "^3.0.9", - "@vitest/runner": "3.0.9", - "@vitest/snapshot": "3.0.9", - "@vitest/spy": "3.0.9", - "@vitest/utils": "3.0.9", - "chai": "^5.2.0", - "debug": "^4.4.0", - "expect-type": "^1.1.0", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "std-env": "^3.8.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.9", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.9", - "@vitest/ui": "3.0.9", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest-canvas-mock": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/vitest-canvas-mock/-/vitest-canvas-mock-0.3.3.tgz", - "integrity": "sha512-3P968tYBpqYyzzOaVtqnmYjqbe13576/fkjbDEJSfQAkHtC5/UjuRHOhFEN/ZV5HVZIkaROBUWgazDKJ+Ibw+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-canvas-mock": "~2.5.2" - }, - "peerDependencies": { - "vitest": "*" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/watskeburt": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.1.0.tgz", - "integrity": "sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==", - "dev": true, - "bin": { - "watskeburt": "dist/run-cli.js" - }, - "engines": { - "node": "^18||>=20" - } - }, - "node_modules/webfontloader": { - "version": "1.6.28", - "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz", - "integrity": "sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==", - "license": "Apache-2.0" - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index e2b35026d0c..43736c10db6 100644 --- a/package.json +++ b/package.json @@ -22,24 +22,19 @@ "docs": "typedoc", "depcruise": "depcruise src test", "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", - "postinstall": "npx lefthook install && npx lefthook run post-merge", - "update-version:patch": "npm version patch --force --no-git-tag-version", - "update-version:minor": "npm version minor --force --no-git-tag-version", + "postinstall": "lefthook install && lefthook run post-merge", + "update-version:patch": "pnpm version patch --force --no-git-tag-version", + "update-version:minor": "pnpm version minor --force --no-git-tag-version", "update-locales:remote": "git submodule update --progress --init --recursive --force --remote" }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@eslint/js": "^9.23.0", + "@biomejs/biome": "2.0.0", "@hpcc-js/wasm": "^2.22.4", - "@stylistic/eslint-plugin-ts": "^4.1.0", "@types/jsdom": "^21.1.7", "@types/node": "^22.13.14", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", "@vitest/coverage-istanbul": "^3.0.9", + "chalk": "^5.4.1", "dependency-cruiser": "^16.3.10", - "eslint": "^9.23.0", - "eslint-plugin-import-x": "^4.9.4", "inquirer": "^12.4.2", "jsdom": "^26.0.0", "lefthook": "^1.11.5", @@ -47,7 +42,6 @@ "phaser3spectorjs": "^0.0.8", "typedoc": "^0.28.1", "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", "vite": "^6.3.4", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000000..7d218bc00eb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3910 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@material/material-color-utilities': + specifier: ^0.2.7 + version: 0.2.7 + compare-versions: + specifier: ^6.1.1 + version: 6.1.1 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + i18next: + specifier: ^24.2.2 + version: 24.2.3(typescript@5.8.2) + i18next-browser-languagedetector: + specifier: ^8.0.4 + version: 8.0.4 + i18next-http-backend: + specifier: ^3.0.2 + version: 3.0.2 + i18next-korean-postposition-processor: + specifier: ^1.0.0 + version: 1.0.0(i18next@24.2.3(typescript@5.8.2)) + json-stable-stringify: + specifier: ^1.2.0 + version: 1.2.1 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + phaser: + specifier: ^3.88.2 + version: 3.88.2 + phaser3-rex-plugins: + specifier: ^1.80.15 + version: 1.80.15(graphology-types@0.24.8) + devDependencies: + '@biomejs/biome': + specifier: 2.0.0 + version: 2.0.0 + '@hpcc-js/wasm': + specifier: ^2.22.4 + version: 2.22.4 + '@types/jsdom': + specifier: ^21.1.7 + version: 21.1.7 + '@types/node': + specifier: ^22.13.14 + version: 22.13.14 + '@vitest/coverage-istanbul': + specifier: ^3.0.9 + version: 3.0.9(vitest@3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1)) + chalk: + specifier: ^5.4.1 + version: 5.4.1 + dependency-cruiser: + specifier: ^16.3.10 + version: 16.3.10 + inquirer: + specifier: ^12.4.2 + version: 12.5.0(@types/node@22.13.14) + jsdom: + specifier: ^26.0.0 + version: 26.0.0 + lefthook: + specifier: ^1.11.5 + version: 1.11.6 + msw: + specifier: ^2.7.3 + version: 2.7.3(@types/node@22.13.14)(typescript@5.8.2) + phaser3spectorjs: + specifier: ^0.0.8 + version: 0.0.8 + typedoc: + specifier: ^0.28.1 + version: 0.28.1(typescript@5.8.2) + typescript: + specifier: ^5.8.2 + version: 5.8.2 + vite: + specifier: ^6.3.4 + version: 6.3.4(@types/node@22.13.14)(yaml@2.7.1) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.2)(vite@6.3.4(@types/node@22.13.14)(yaml@2.7.1)) + vitest: + specifier: ^3.0.9 + version: 3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1) + vitest-canvas-mock: + specifier: ^0.3.3 + version: 0.3.3(vitest@3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1)) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@asamuzakjp/css-color@3.1.1': + resolution: {integrity: sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.9': + resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.9': + resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.10': + resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.8': + resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.9': + resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.8': + resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.0.0': + resolution: {integrity: sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.0.0': + resolution: {integrity: sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.0.0': + resolution: {integrity: sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.0.0': + resolution: {integrity: sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.0.0': + resolution: {integrity: sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.0.0': + resolution: {integrity: sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.0.0': + resolution: {integrity: sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.0.0': + resolution: {integrity: sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.0.0': + resolution: {integrity: sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + + '@csstools/color-helpers@5.0.2': + resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.2': + resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-color-parser@3.0.8': + resolution: {integrity: sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.25.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@gerrit0/mini-shiki@3.2.2': + resolution: {integrity: sha512-vaZNGhGLKMY14HbF53xxHNgFO9Wz+t5lTlGNpl2N9xFiKQ0I5oIe0vKjU9dh7Nb3Dw6lZ7wqUE0ri+zcdpnK+Q==} + + '@hpcc-js/wasm@2.22.4': + resolution: {integrity: sha512-58JkRkxZffiBAbZhc7z+9iaaAOmn1cyxLL3rRwsUvco/I0Wwb7uVAlHM9HiU6XASe2k11jrIjCFff1t9QKjlqg==} + hasBin: true + + '@inquirer/checkbox@4.1.4': + resolution: {integrity: sha512-d30576EZdApjAMceijXA5jDzRQHT/MygbC+J8I7EqA6f/FRpYxlRtRJbHF8gHeWYeSdOuTEJqonn7QLB1ELezA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.8': + resolution: {integrity: sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.9': + resolution: {integrity: sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.9': + resolution: {integrity: sha512-8HjOppAxO7O4wV1ETUlJFg6NDjp/W2NP5FB9ZPAcinAlNT4ZIWOLe2pUVwmmPRSV0NMdI5r/+lflN55AwZOKSw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.11': + resolution: {integrity: sha512-OZSUW4hFMW2TYvX/Sv+NnOZgO8CHT2TU1roUCUIF2T+wfw60XFRRp9MRUPCT06cRnKL+aemt2YmTWwt7rOrNEA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.11': + resolution: {integrity: sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==} + engines: {node: '>=18'} + + '@inquirer/input@4.1.8': + resolution: {integrity: sha512-WXJI16oOZ3/LiENCAxe8joniNp8MQxF6Wi5V+EBbVA0ZIOpFcL4I9e7f7cXse0HJeIPCWO8Lcgnk98juItCi7Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.11': + resolution: {integrity: sha512-pQK68CsKOgwvU2eA53AG/4npRTH2pvs/pZ2bFvzpBhrznh8Mcwt19c+nMO7LHRr3Vreu1KPhNBF3vQAKrjIulw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.11': + resolution: {integrity: sha512-dH6zLdv+HEv1nBs96Case6eppkRggMe8LoOTl30+Gq5Wf27AO/vHFgStTVz4aoevLdNXqwE23++IXGw4eiOXTg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.4.0': + resolution: {integrity: sha512-EZiJidQOT4O5PYtqnu1JbF0clv36oW2CviR66c7ma4LsupmmQlUwmdReGKRp456OWPWMz3PdrPiYg3aCk3op2w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.0.11': + resolution: {integrity: sha512-uAYtTx0IF/PqUAvsRrF3xvnxJV516wmR6YVONOmCWJbbt87HcDHLfL9wmBQFbNJRv5kCjdYKrZcavDkH3sVJPg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.0.11': + resolution: {integrity: sha512-9CWQT0ikYcg6Ls3TOa7jljsD7PgjcsYEM0bYE+Gkz+uoW9u8eaJCRHJKkucpRE5+xKtaaDbrND+nPDoxzjYyew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.1.0': + resolution: {integrity: sha512-z0a2fmgTSRN+YBuiK1ROfJ2Nvrpij5lVN3gPDkQGhavdvIVGHGW29LwYZfM/j42Ai2hUghTI/uoBuTbrJk42bA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.5': + resolution: {integrity: sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@material/material-color-utilities@0.2.7': + resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==} + + '@mswjs/interceptors@0.37.6': + resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} + engines: {node: '>=18'} + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/rollup-android-arm-eabi@4.40.1': + resolution: {integrity: sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.1': + resolution: {integrity: sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.1': + resolution: {integrity: sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.1': + resolution: {integrity: sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.1': + resolution: {integrity: sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.1': + resolution: {integrity: sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': + resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.1': + resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.1': + resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.1': + resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': + resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': + resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.1': + resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.1': + resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.1': + resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.1': + resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.1': + resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.1': + resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.1': + resolution: {integrity: sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.1': + resolution: {integrity: sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==} + cpu: [x64] + os: [win32] + + '@shikijs/engine-oniguruma@3.2.1': + resolution: {integrity: sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==} + + '@shikijs/langs@3.2.1': + resolution: {integrity: sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==} + + '@shikijs/themes@3.2.1': + resolution: {integrity: sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==} + + '@shikijs/types@3.2.1': + resolution: {integrity: sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/node@22.13.14': + resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==} + + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@vitest/coverage-istanbul@3.0.9': + resolution: {integrity: sha512-/TXh2qmOhclmVPjOnPTpIO4Xr6l2P5EwyXQygenwq4/ZQ/vPsrz+GCRZF9kBeQi6xrGcHv368Si9PGImWQawVg==} + peerDependencies: + vitest: 3.0.9 + + '@vitest/expect@3.0.9': + resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==} + + '@vitest/mocker@3.0.9': + resolution: {integrity: sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.0.9': + resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==} + + '@vitest/runner@3.0.9': + resolution: {integrity: sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==} + + '@vitest/snapshot@3.0.9': + resolution: {integrity: sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==} + + '@vitest/spy@3.0.9': + resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==} + + '@vitest/utils@3.0.9': + resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + + acorn-jsx-walk@2.0.0: + resolution: {integrity: sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-loose@8.4.0: + resolution: {integrity: sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + browserslist@4.23.2: + resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001642: + resolution: {integrity: sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==} + + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + cssfontparser@1.2.1: + resolution: {integrity: sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==} + + cssstyle@4.3.0: + resolution: {integrity: sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==} + engines: {node: '>=18'} + + dagre@0.8.5: + resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dependency-cruiser@16.3.10: + resolution: {integrity: sha512-WkCnibHBfvaiaQ+S46LZ6h4AR6oj42Vsf5/0Vgtrwdwn7ZekMJdZ/ALoTwNp/RaGlKW+MbV/fhSZOvmhAWVWzQ==} + engines: {node: ^18.17||>=20} + hasBin: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.4.830: + resolution: {integrity: sha512-TrPKKH20HeN0J1LHzsYLs2qwXrp8TF4nHdu4sq61ozGbzMpWhI7iIOPYPPkxeq1azMT9PZ8enPFcftbs/Npcjg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@3.1.2: + resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphlib@2.1.8: + resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} + + graphology-types@0.24.8: + resolution: {integrity: sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==} + + graphology@0.25.4: + resolution: {integrity: sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==} + peerDependencies: + graphology-types: '>=0.24.0' + + graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + i18next-browser-languagedetector@8.0.4: + resolution: {integrity: sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==} + + i18next-http-backend@2.7.3: + resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} + + i18next-http-backend@3.0.2: + resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==} + + i18next-korean-postposition-processor@1.0.0: + resolution: {integrity: sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==} + peerDependencies: + i18next: '>=8.4.0' + + i18next@22.5.1: + resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} + + i18next@24.2.3: + resolution: {integrity: sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@12.5.0: + resolution: {integrity: sha512-aiBBq5aKF1k87MTxXDylLfwpRwToShiHrSv4EmB07EYyLgmnjEz5B3rn0aGw1X3JA/64Ngf2T54oGwc+BCsPIQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jest-canvas-mock@2.5.2: + resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@26.0.0: + resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify@1.2.1: + resolution: {integrity: sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==} + engines: {node: '>= 0.4'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonify@0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lefthook-darwin-arm64@1.11.6: + resolution: {integrity: sha512-gWgdWrKgZgX+bKc6Vs/x7JkO+58lLOpRzpteLx//82D0MKVPlNZwjd4zz4AbIBXtM4Hcj+6gSsOzQ7QDXxjVvQ==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@1.11.6: + resolution: {integrity: sha512-Ia0TjTKuYcSaDTuoCnbWtpPZ2VEoKzgn33OB90VjNaSVs4ooE0PIdpO+w00x1elqIaf1pbrpq6HgeB26Du8KbQ==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@1.11.6: + resolution: {integrity: sha512-PxIwj+hmjLahyzEmcIfalIBDhgklAQCavwM4sGCgbzDi4/+VQX+4aEs4pQqtd7v3aohmjtO/4n2emzTI8donww==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@1.11.6: + resolution: {integrity: sha512-3o1lMKxz1VtWaP/o117wgUn3ZOpefMoSf+8LuiTzI3/PDprIuzgyw2nXKlBZAMDpNPHMNnJeQNts9XLMRmkldg==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@1.11.6: + resolution: {integrity: sha512-nKPFZ5cA9f5tVn0ybDVqcXXlpTHZqo05N4KQRhWTj5Nem+JoD2YzJIlvZhdJhUrldERqj6deDMXChH5T3z4Rrw==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@1.11.6: + resolution: {integrity: sha512-naN8dllLCOEeP+wznLnq+oXrs1dvt/iMLkcl+pOPWLqFccPfDiHzr8V8GslaTa+rSFsAnvjR7SJIOi5C29xedA==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@1.11.6: + resolution: {integrity: sha512-dPxhJfYQ667T+U3pz1+O3mTRNHzXH/BvPlXSH+oy8uiSry4AtVNRXkVvXPUcpLlrAy6HuFYodsrpCIlWFeYwiQ==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@1.11.6: + resolution: {integrity: sha512-9D26kcSsjiW4D0AuVDdi+0ZqrsOzRWOpMS/kcUbLfrU99yCvma0rMTqKbbDMkVur/znS7qL53oGahXCXDNA+IQ==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@1.11.6: + resolution: {integrity: sha512-xdCenr4+BFnfBEhiXj6GJp02EPmcwTAGa7NYm6hVTfDwGXw24tuLv7lpnGjgK3kovN6EukgLH1FYkeyDOBEMnA==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@1.11.6: + resolution: {integrity: sha512-Fg2GzLhzeDV/GX8+ydrI0wBOytQWpPkNdngx+a8B/feCDbwjAiFklDG5oV4ytuWrtg1JPEEWLJd6nHefj4wtHA==} + cpu: [x64] + os: [win32] + + lefthook@1.11.6: + resolution: {integrity: sha512-j0VmMM50WlPDassmgvapRum9po29Tv1BXzBNFpzGkk9E91CEG9jKik/OHyH/r/na+q8qNIUUyPL6QQuTN/UhQQ==} + hasBin: true + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + memoize@10.0.0: + resolution: {integrity: sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==} + engines: {node: '>=18'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + moo-color@1.0.3: + resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msw@2.7.3: + resolution: {integrity: sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + 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.17: + resolution: {integrity: sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==} + + nwsapi@2.2.20: + resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + obliterator@2.0.5: + resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + papaparse@5.4.1: + resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} + + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + phaser3-rex-plugins@1.80.15: + resolution: {integrity: sha512-Ur973N1W5st6XEYBcJko8eTcEbdDHMM+m7VqvT3j/EJeJwYyJ3bVb33JJDsFgefk3A2iAz2itP/UY7CzxJOJVA==} + + phaser3spectorjs@0.0.8: + resolution: {integrity: sha512-0dSO7/aMjEUPrp5EcjRvRRsEf+jXDbmzalPeJ6VtTB2Pn1PeaKc+qlL/DmO3l1Dvc5lkzc+Sil1Ta+Hkyi5cbA==} + + phaser@3.88.2: + resolution: {integrity: sha512-UBgd2sAFuRJbF2xKaQ5jpMWB8oETncChLnymLGHcrnT53vaqiGrQWbUKUDBawKLm24sghjKo4Bf+/xfv8espZQ==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + rollup@4.40.1: + resolution: {integrity: sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-regex@2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + 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==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + teamcity-service-messages@0.1.14: + resolution: {integrity: sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==} + + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.85: + resolution: {integrity: sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==} + + tldts@6.1.85: + resolution: {integrity: sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==} + hasBin: true + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.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@5.1.0: + resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==} + engines: {node: '>=18'} + + tsconfck@3.1.1: + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths-webpack-plugin@4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.26.1: + resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} + engines: {node: '>=16'} + + typedoc@0.28.1: + resolution: {integrity: sha512-Mn2VPNMaxoe/hlBiLriG4U55oyAa3Xo+8HbtEwV7F5WEOPXqtxzGuMZhJYHaqFJpajeQ6ZDUC2c990NAtTbdgw==} + engines: {node: '>= 18', pnpm: '>= 10'} + hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-node@3.0.9: + resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@6.3.4: + resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest-canvas-mock@0.3.3: + resolution: {integrity: sha512-3P968tYBpqYyzzOaVtqnmYjqbe13576/fkjbDEJSfQAkHtC5/UjuRHOhFEN/ZV5HVZIkaROBUWgazDKJ+Ibw+Q==} + peerDependencies: + vitest: '*' + + vitest@3.0.9: + resolution: {integrity: sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.9 + '@vitest/ui': 3.0.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + watskeburt@4.1.0: + resolution: {integrity: sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==} + engines: {node: ^18||>=20} + hasBin: true + + webfontloader@1.6.28: + resolution: {integrity: sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + 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 + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + 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@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@asamuzakjp/css-color@3.1.1': + dependencies: + '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + lru-cache: 10.4.3 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.24.9': {} + + '@babel/core@7.24.9': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.24.10 + '@babel/helper-compilation-targets': 7.24.8 + '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.24.8 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.24.10': + dependencies: + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.24.8': + dependencies: + '@babel/compat-data': 7.24.9 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.8 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)': + dependencies: + '@babel/core': 7.24.9 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.24.8 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + + '@babel/parser@7.27.5': + dependencies: + '@babel/types': 7.27.6 + + '@babel/runtime@7.27.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + + '@babel/traverse@7.24.8': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.24.10 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@biomejs/biome@2.0.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.0.0 + '@biomejs/cli-darwin-x64': 2.0.0 + '@biomejs/cli-linux-arm64': 2.0.0 + '@biomejs/cli-linux-arm64-musl': 2.0.0 + '@biomejs/cli-linux-x64': 2.0.0 + '@biomejs/cli-linux-x64-musl': 2.0.0 + '@biomejs/cli-win32-arm64': 2.0.0 + '@biomejs/cli-win32-x64': 2.0.0 + + '@biomejs/cli-darwin-arm64@2.0.0': + optional: true + + '@biomejs/cli-darwin-x64@2.0.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.0.0': + optional: true + + '@biomejs/cli-linux-arm64@2.0.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.0.0': + optional: true + + '@biomejs/cli-linux-x64@2.0.0': + optional: true + + '@biomejs/cli-win32-arm64@2.0.0': + optional: true + + '@biomejs/cli-win32-x64@2.0.0': + optional: true + + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + + '@csstools/color-helpers@5.0.2': {} + + '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/color-helpers': 5.0.2 + '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + + '@esbuild/aix-ppc64@0.25.2': + optional: true + + '@esbuild/android-arm64@0.25.2': + optional: true + + '@esbuild/android-arm@0.25.2': + optional: true + + '@esbuild/android-x64@0.25.2': + optional: true + + '@esbuild/darwin-arm64@0.25.2': + optional: true + + '@esbuild/darwin-x64@0.25.2': + optional: true + + '@esbuild/freebsd-arm64@0.25.2': + optional: true + + '@esbuild/freebsd-x64@0.25.2': + optional: true + + '@esbuild/linux-arm64@0.25.2': + optional: true + + '@esbuild/linux-arm@0.25.2': + optional: true + + '@esbuild/linux-ia32@0.25.2': + optional: true + + '@esbuild/linux-loong64@0.25.2': + optional: true + + '@esbuild/linux-mips64el@0.25.2': + optional: true + + '@esbuild/linux-ppc64@0.25.2': + optional: true + + '@esbuild/linux-riscv64@0.25.2': + optional: true + + '@esbuild/linux-s390x@0.25.2': + optional: true + + '@esbuild/linux-x64@0.25.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.2': + optional: true + + '@esbuild/netbsd-x64@0.25.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.2': + optional: true + + '@esbuild/openbsd-x64@0.25.2': + optional: true + + '@esbuild/sunos-x64@0.25.2': + optional: true + + '@esbuild/win32-arm64@0.25.2': + optional: true + + '@esbuild/win32-ia32@0.25.2': + optional: true + + '@esbuild/win32-x64@0.25.2': + optional: true + + '@gerrit0/mini-shiki@3.2.2': + dependencies: + '@shikijs/engine-oniguruma': 3.2.1 + '@shikijs/langs': 3.2.1 + '@shikijs/themes': 3.2.1 + '@shikijs/types': 3.2.1 + '@shikijs/vscode-textmate': 10.0.2 + + '@hpcc-js/wasm@2.22.4': + dependencies: + yargs: 17.7.2 + + '@inquirer/checkbox@4.1.4(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.5(@types/node@22.13.14) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/confirm@5.1.8(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/core@10.1.9(@types/node@22.13.14)': + dependencies: + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.5(@types/node@22.13.14) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/editor@4.2.9(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + external-editor: 3.1.0 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/expand@4.0.11(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/figures@1.0.11': {} + + '@inquirer/input@4.1.8(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/number@3.0.11(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/password@4.0.11(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/prompts@7.4.0(@types/node@22.13.14)': + dependencies: + '@inquirer/checkbox': 4.1.4(@types/node@22.13.14) + '@inquirer/confirm': 5.1.8(@types/node@22.13.14) + '@inquirer/editor': 4.2.9(@types/node@22.13.14) + '@inquirer/expand': 4.0.11(@types/node@22.13.14) + '@inquirer/input': 4.1.8(@types/node@22.13.14) + '@inquirer/number': 3.0.11(@types/node@22.13.14) + '@inquirer/password': 4.0.11(@types/node@22.13.14) + '@inquirer/rawlist': 4.0.11(@types/node@22.13.14) + '@inquirer/search': 3.0.11(@types/node@22.13.14) + '@inquirer/select': 4.1.0(@types/node@22.13.14) + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/rawlist@4.0.11(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/search@3.0.11(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.5(@types/node@22.13.14) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/select@4.1.0(@types/node@22.13.14)': + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.5(@types/node@22.13.14) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.13.14 + + '@inquirer/type@3.0.5(@types/node@22.13.14)': + optionalDependencies: + '@types/node': 22.13.14 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@material/material-color-utilities@0.2.7': {} + + '@mswjs/interceptors@0.37.6': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.40.1': + optional: true + + '@rollup/rollup-android-arm64@4.40.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.1': + optional: true + + '@rollup/rollup-darwin-x64@4.40.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.1': + optional: true + + '@shikijs/engine-oniguruma@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + + '@shikijs/themes@3.2.1': + dependencies: + '@shikijs/types': 3.2.1 + + '@shikijs/types@3.2.1': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.7': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 22.13.14 + '@types/tough-cookie': 4.0.5 + parse5: 7.2.1 + + '@types/node@22.13.14': + dependencies: + undici-types: 6.20.0 + + '@types/statuses@2.0.5': {} + + '@types/tough-cookie@4.0.5': {} + + '@types/unist@3.0.3': {} + + '@vitest/coverage-istanbul@3.0.9(vitest@3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1))': + dependencies: + '@istanbuljs/schema': 0.1.3 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magicast: 0.3.5 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.0.9': + dependencies: + '@vitest/spy': 3.0.9 + '@vitest/utils': 3.0.9 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.0.9(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(vite@6.3.4(@types/node@22.13.14)(yaml@2.7.1))': + dependencies: + '@vitest/spy': 3.0.9 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + msw: 2.7.3(@types/node@22.13.14)(typescript@5.8.2) + vite: 6.3.4(@types/node@22.13.14)(yaml@2.7.1) + + '@vitest/pretty-format@3.0.9': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.0.9': + dependencies: + '@vitest/utils': 3.0.9 + pathe: 2.0.3 + + '@vitest/snapshot@3.0.9': + dependencies: + '@vitest/pretty-format': 3.0.9 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.0.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@3.0.9': + dependencies: + '@vitest/pretty-format': 3.0.9 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + acorn-jsx-walk@2.0.0: {} + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn-loose@8.4.0: + dependencies: + acorn: 8.12.1 + + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + agent-base@7.1.3: {} + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + asynckit@0.4.0: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + browserslist@4.23.2: + dependencies: + caniuse-lite: 1.0.30001642 + electron-to-chromium: 1.4.830 + node-releases: 2.0.17 + update-browserslist-db: 1.1.0(browserslist@4.23.2) + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001642: {} + + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chardet@0.7.0: {} + + check-error@2.1.1: {} + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + compare-versions@6.1.1: {} + + convert-source-map@2.0.0: {} + + cookie@0.7.2: {} + + core-util-is@1.0.3: {} + + cross-fetch@4.0.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-js@4.2.0: {} + + cssfontparser@1.2.1: {} + + cssstyle@4.3.0: + dependencies: + '@asamuzakjp/css-color': 3.1.1 + rrweb-cssom: 0.8.0 + + dagre@0.8.5: + dependencies: + graphlib: 2.1.8 + lodash: 4.17.21 + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decimal.js@10.4.3: {} + + deep-eql@5.0.2: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + delayed-stream@1.0.0: {} + + dependency-cruiser@16.3.10: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + acorn-jsx-walk: 2.0.0 + acorn-loose: 8.4.0 + acorn-walk: 8.3.3 + ajv: 8.17.1 + commander: 12.1.0 + enhanced-resolve: 5.17.1 + ignore: 5.3.1 + interpret: 3.1.1 + is-installed-globally: 1.0.0 + json5: 2.2.3 + memoize: 10.0.0 + picocolors: 1.0.1 + picomatch: 4.0.2 + prompts: 2.4.2 + rechoir: 0.8.0 + safe-regex: 2.1.1 + semver: 7.7.1 + teamcity-service-messages: 0.1.14 + tsconfig-paths-webpack-plugin: 4.1.0 + watskeburt: 4.1.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.4.830: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.17.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + entities@4.5.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.6.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.25.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 + + escalade@3.1.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.7 + + eventemitter3@3.1.2: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + expect-type@1.1.0: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-uri@3.0.1: {} + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + foreground-child@3.2.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob@10.4.5: + dependencies: + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globals@11.12.0: {} + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphlib@2.1.8: + dependencies: + lodash: 4.17.21 + + graphology-types@0.24.8: {} + + graphology@0.25.4(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-types: 0.24.8 + obliterator: 2.0.5 + + graphql@16.9.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + headers-polyfill@4.0.3: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-escaper@2.0.2: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + i18next-browser-languagedetector@8.0.4: + dependencies: + '@babel/runtime': 7.27.0 + + i18next-http-backend@2.7.3: + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + + i18next-http-backend@3.0.2: + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + + i18next-korean-postposition-processor@1.0.0(i18next@24.2.3(typescript@5.8.2)): + dependencies: + i18next: 24.2.3(typescript@5.8.2) + + i18next@22.5.1: + dependencies: + '@babel/runtime': 7.27.0 + + i18next@24.2.3(typescript@5.8.2): + dependencies: + '@babel/runtime': 7.27.0 + optionalDependencies: + typescript: 5.8.2 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.1: {} + + immediate@3.0.6: {} + + inherits@2.0.4: {} + + ini@4.1.1: {} + + inquirer@12.5.0(@types/node@22.13.14): + dependencies: + '@inquirer/core': 10.1.9(@types/node@22.13.14) + '@inquirer/prompts': 7.4.0(@types/node@22.13.14) + '@inquirer/type': 3.0.5(@types/node@22.13.14) + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 3.0.0 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 22.13.14 + + interpret@3.1.1: {} + + is-core-module@2.15.0: + dependencies: + hasown: 2.0.2 + + is-fullwidth-code-point@3.0.0: {} + + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + + is-node-process@1.2.0: {} + + is-path-inside@4.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.24.9 + '@babel/parser': 7.27.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.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@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jest-canvas-mock@2.5.2: + dependencies: + cssfontparser: 1.2.1 + moo-color: 1.0.3 + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@26.0.0: + dependencies: + cssstyle: 4.3.0 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.2 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.20 + parse5: 7.2.1 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@2.5.2: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + isarray: 2.0.5 + jsonify: 0.0.1 + object-keys: 1.1.1 + + json5@2.2.3: {} + + jsonify@0.0.1: {} + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + kleur@3.0.3: {} + + lefthook-darwin-arm64@1.11.6: + optional: true + + lefthook-darwin-x64@1.11.6: + optional: true + + lefthook-freebsd-arm64@1.11.6: + optional: true + + lefthook-freebsd-x64@1.11.6: + optional: true + + lefthook-linux-arm64@1.11.6: + optional: true + + lefthook-linux-x64@1.11.6: + optional: true + + lefthook-openbsd-arm64@1.11.6: + optional: true + + lefthook-openbsd-x64@1.11.6: + optional: true + + lefthook-windows-arm64@1.11.6: + optional: true + + lefthook-windows-x64@1.11.6: + optional: true + + lefthook@1.11.6: + optionalDependencies: + lefthook-darwin-arm64: 1.11.6 + lefthook-darwin-x64: 1.11.6 + lefthook-freebsd-arm64: 1.11.6 + lefthook-freebsd-x64: 1.11.6 + lefthook-linux-arm64: 1.11.6 + lefthook-linux-x64: 1.11.6 + lefthook-openbsd-arm64: 1.11.6 + lefthook-openbsd-x64: 1.11.6 + lefthook-windows-arm64: 1.11.6 + lefthook-windows-x64: 1.11.6 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + lodash@4.17.21: {} + + loupe@3.1.3: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lunr@2.3.9: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.1 + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + math-intrinsics@1.1.0: {} + + mdurl@2.0.0: {} + + memoize@10.0.0: + dependencies: + mimic-function: 5.0.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-function@5.0.1: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + moo-color@1.0.3: + dependencies: + color-name: 1.1.4 + + ms@2.1.3: {} + + msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.8(@types/node@22.13.14) + '@mswjs/interceptors': 0.37.6 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.9.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.26.1 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + + mustache@4.2.0: {} + + mute-stream@2.0.0: {} + + nanoid@3.3.11: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.17: {} + + nwsapi@2.2.20: {} + + object-keys@1.1.1: {} + + obliterator@2.0.5: {} + + os-tmpdir@1.0.2: {} + + outvariant@1.4.3: {} + + package-json-from-dist@1.0.0: {} + + pako@1.0.11: {} + + papaparse@5.4.1: {} + + parse5@7.2.1: + dependencies: + entities: 4.5.0 + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.0: {} + + phaser3-rex-plugins@1.80.15(graphology-types@0.24.8): + dependencies: + dagre: 0.8.5 + eventemitter3: 3.1.2 + graphology: 0.25.4(graphology-types@0.24.8) + i18next: 22.5.1 + i18next-http-backend: 2.7.3 + js-yaml: 4.1.0 + mustache: 4.2.0 + papaparse: 5.4.1 + webfontloader: 1.6.28 + transitivePeerDependencies: + - encoding + - graphology-types + + phaser3spectorjs@0.0.8: {} + + phaser@3.88.2: + dependencies: + eventemitter3: 5.0.1 + + picocolors@1.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.2: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + process-nextick-args@2.0.1: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + psl@1.9.0: {} + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + querystringify@2.2.0: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + rechoir@0.8.0: + dependencies: + resolve: 1.22.8 + + regenerator-runtime@0.14.1: {} + + regexp-tree@0.1.27: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.40.1: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.1 + '@rollup/rollup-android-arm64': 4.40.1 + '@rollup/rollup-darwin-arm64': 4.40.1 + '@rollup/rollup-darwin-x64': 4.40.1 + '@rollup/rollup-freebsd-arm64': 4.40.1 + '@rollup/rollup-freebsd-x64': 4.40.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.1 + '@rollup/rollup-linux-arm-musleabihf': 4.40.1 + '@rollup/rollup-linux-arm64-gnu': 4.40.1 + '@rollup/rollup-linux-arm64-musl': 4.40.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.1 + '@rollup/rollup-linux-riscv64-gnu': 4.40.1 + '@rollup/rollup-linux-riscv64-musl': 4.40.1 + '@rollup/rollup-linux-s390x-gnu': 4.40.1 + '@rollup/rollup-linux-x64-gnu': 4.40.1 + '@rollup/rollup-linux-x64-musl': 4.40.1 + '@rollup/rollup-win32-arm64-msvc': 4.40.1 + '@rollup/rollup-win32-ia32-msvc': 4.40.1 + '@rollup/rollup-win32-x64-msvc': 4.40.1 + fsevents: 2.3.3 + + rrweb-cssom@0.8.0: {} + + run-async@3.0.0: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.1.2: {} + + safe-regex@2.1.1: + dependencies: + regexp-tree: 0.1.27 + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + semver@6.3.1: {} + + semver@7.7.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + statuses@2.0.1: {} + + std-env@3.8.0: {} + + strict-event-emitter@0.5.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@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-bom@3.0.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-tree@3.2.4: {} + + tapable@2.2.1: {} + + teamcity-service-messages@0.1.14: {} + + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + + tinyspy@3.0.2: {} + + tldts-core@6.1.85: {} + + tldts@6.1.85: + dependencies: + tldts-core: 6.1.85 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tough-cookie@4.1.4: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.85 + + tr46@0.0.3: {} + + tr46@5.1.0: + dependencies: + punycode: 2.3.1 + + tsconfck@3.1.1(typescript@5.8.2): + optionalDependencies: + typescript: 5.8.2 + + tsconfig-paths-webpack-plugin@4.1.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.17.1 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-fest@0.21.3: {} + + type-fest@4.26.1: {} + + typedoc@0.28.1(typescript@5.8.2): + dependencies: + '@gerrit0/mini-shiki': 3.2.2 + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.8.2 + yaml: 2.7.1 + + typescript@5.8.2: {} + + uc.micro@2.1.0: {} + + undici-types@6.20.0: {} + + universalify@0.2.0: {} + + update-browserslist-db@1.1.0(browserslist@4.23.2): + dependencies: + browserslist: 4.23.2 + escalade: 3.1.2 + picocolors: 1.1.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + vite-node@3.0.9(@types/node@22.13.14)(yaml@2.7.1): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.3.4(@types/node@22.13.14)(yaml@2.7.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@6.3.4(@types/node@22.13.14)(yaml@2.7.1)): + dependencies: + debug: 4.4.0 + globrex: 0.1.2 + tsconfck: 3.1.1(typescript@5.8.2) + optionalDependencies: + vite: 6.3.4(@types/node@22.13.14)(yaml@2.7.1) + transitivePeerDependencies: + - supports-color + - typescript + + vite@6.3.4(@types/node@22.13.14)(yaml@2.7.1): + dependencies: + esbuild: 0.25.2 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.1 + tinyglobby: 0.2.13 + optionalDependencies: + '@types/node': 22.13.14 + fsevents: 2.3.3 + yaml: 2.7.1 + + vitest-canvas-mock@0.3.3(vitest@3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1)): + dependencies: + jest-canvas-mock: 2.5.2 + vitest: 3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1) + + vitest@3.0.9(@types/node@22.13.14)(jsdom@26.0.0)(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(yaml@2.7.1): + dependencies: + '@vitest/expect': 3.0.9 + '@vitest/mocker': 3.0.9(msw@2.7.3(@types/node@22.13.14)(typescript@5.8.2))(vite@6.3.4(@types/node@22.13.14)(yaml@2.7.1)) + '@vitest/pretty-format': 3.0.9 + '@vitest/runner': 3.0.9 + '@vitest/snapshot': 3.0.9 + '@vitest/spy': 3.0.9 + '@vitest/utils': 3.0.9 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.3.4(@types/node@22.13.14)(yaml@2.7.1) + vite-node: 3.0.9(@types/node@22.13.14)(yaml@2.7.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.13.14 + jsdom: 26.0.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + watskeburt@4.1.0: {} + + webfontloader@1.6.28: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.0 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + 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: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + ws@8.18.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@2.7.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yoctocolors-cjs@2.1.2: {} diff --git a/public/images/ui/legacy/summary_stats.png b/public/images/ui/legacy/summary_stats.png index 53c6a18caab..de8baecc7d4 100644 Binary files a/public/images/ui/legacy/summary_stats.png and b/public/images/ui/legacy/summary_stats.png differ diff --git a/public/images/ui/summary_stats.png b/public/images/ui/summary_stats.png index b87d67b864a..bcbc6809852 100644 Binary files a/public/images/ui/summary_stats.png and b/public/images/ui/summary_stats.png differ diff --git a/scripts/create-test/create-test.js b/scripts/create-test/create-test.js index d5cac5cd408..3c667049658 100644 --- a/scripts/create-test/create-test.js +++ b/scripts/create-test/create-test.js @@ -1,7 +1,7 @@ /** * This script creates a test boilerplate file in the appropriate * directory based on the type selected. - * @example npm run test:create + * @example pnpm test:create */ import chalk from "chalk"; diff --git a/src/@types/ability-types.ts b/src/@types/ability-types.ts index 6f21a012b64..18516fadd40 100644 --- a/src/@types/ability-types.ts +++ b/src/@types/ability-types.ts @@ -1,14 +1,14 @@ -import type { AbAttr } from "#app/data/abilities/ability"; import type Move from "#app/data/moves/move"; import type Pokemon from "#app/field/pokemon"; import type { BattleStat } from "#enums/stat"; import type { AbAttrConstructorMap } from "#app/data/abilities/ability"; -// Intentionally re-export all types from the ability attributes module +// intentionally re-export all types from abilities to have this be the centralized place to import ability types export type * from "#app/data/abilities/ability"; -export type AbAttrApplyFunc = (attr: TAttr, passive: boolean, ...args: any[]) => void; -export type AbAttrSuccessFunc = (attr: TAttr, passive: boolean, ...args: any[]) => boolean; +// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment +import type { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; + export type AbAttrCondition = (pokemon: Pokemon) => boolean; export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; @@ -25,3 +25,22 @@ export type AbAttrString = keyof AbAttrConstructorMap; export type AbAttrMap = { [K in keyof AbAttrConstructorMap]: InstanceType; }; + +/** + * Subset of ability attribute classes that may be passed to {@linkcode applyAbAttrs} method + * + * @remarks + * Our AbAttr classes violate Liskov Substitution Principle. + * + * AbAttrs that are not in this have subclasses with apply methods requiring different parameters than + * the base apply method. + * + * Such attributes may not be passed to the {@linkcode applyAbAttrs} method + */ +export type CallableAbAttrString = + | Exclude + | "PreApplyBattlerTagAbAttr"; + +export type AbAttrParamMap = { + [K in keyof AbAttrMap]: Parameters[0]; +}; diff --git a/src/@types/type-helpers.ts b/src/@types/type-helpers.ts new file mode 100644 index 00000000000..2d00b1faf4a --- /dev/null +++ b/src/@types/type-helpers.ts @@ -0,0 +1,34 @@ +/* + * A collection of custom utility types that aid in type checking and ensuring strict type conformity + */ + +// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment +import type { AbAttr } from "./ability-types"; + +/** + * Exactly matches the type of the argument, preventing adding additional properties. + * + * ⚠️ Should never be used with `extends`, as this will nullify the exactness of the type. + * + * As an example, used to ensure that the parameters of {@linkcode AbAttr.canApply} and {@linkcode AbAttr.getTriggerMessage} are compatible with + * the type of the apply method + * + * @typeParam T - The type to match exactly + */ +export type Exact = { + [K in keyof T]: T[K]; +}; + +/** + * Type hint that indicates that the type is intended to be closed to a specific shape. + * Does not actually do anything special, is really just an alias for X. + */ +export type Closed = X; + +/** + * Remove `readonly` from all properties of the provided type + * @typeParam T - The type to make mutable + */ +export type Mutable = { + -readonly [P in keyof T]: T[P]; +}; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b802466ee19..2ac13033412 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -67,7 +67,7 @@ import { modifierTypes } from "./data/data-lists"; import { getModifierPoolForType } from "./utils/modifier-utils"; import { ModifierPoolType } from "#enums/modifier-pool-type"; import AbilityBar from "#app/ui/ability-bar"; -import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "./data/abilities/apply-ab-attrs"; import { allAbilities } from "./data/data-lists"; import type { FixedBattleConfig } from "#app/battle"; import Battle from "#app/battle"; @@ -468,7 +468,7 @@ export default class BattleScene extends SceneBase { true, ); - //@ts-ignore (the defined types in the package are incromplete...) + //@ts-expect-error (the defined types in the package are incromplete...) transition.transit({ mode: "blinds", ease: "Cubic.easeInOut", @@ -894,9 +894,19 @@ export default class BattleScene extends SceneBase { return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; } - getPokemonById(pokemonId: number): Pokemon | null { - const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return (findInParty(this.getPlayerParty()) || findInParty(this.getEnemyParty())) ?? null; + /** + * Return the {@linkcode Pokemon} associated with a given ID. + * @param pokemonId - The ID whose Pokemon will be retrieved. + * @returns The {@linkcode Pokemon} associated with the given id. + * Returns `null` if the ID is `undefined` or not present in either party. + */ + getPokemonById(pokemonId: number | undefined): Pokemon | null { + if (isNullOrUndefined(pokemonId)) { + return null; + } + + const party = (this.getPlayerParty() as Pokemon[]).concat(this.getEnemyParty()); + return party.find(p => p.id === pokemonId) ?? null; } addPlayerPokemon( @@ -1167,7 +1177,7 @@ export default class BattleScene extends SceneBase { this.field.remove(this.currentBattle.mysteryEncounter?.introVisuals, true); } - //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble + //@ts-expect-error - allowing `null` for currentBattle causes a lot of trouble this.currentBattle = null; // TODO: resolve ts-ignore // Reset RNG after end of game or save & quit. @@ -1256,7 +1266,7 @@ export default class BattleScene extends SceneBase { const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); for (const p of playerField) { - applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance); + applyAbAttrs("DoubleBattleChanceAbAttr", { pokemon: p, chance: doubleChance }); } return Math.max(doubleChance.value, 1); } @@ -1461,7 +1471,7 @@ export default class BattleScene extends SceneBase { for (const pokemon of this.getPlayerParty()) { pokemon.resetBattleAndWaveData(); pokemon.resetTera(); - applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon); + applyAbAttrs("PostBattleInitAbAttr", { pokemon }); if ( pokemon.hasSpecies(SpeciesId.TERAPAGOS) || (this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190) @@ -2743,7 +2753,7 @@ export default class BattleScene extends SceneBase { const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { - applyAbAttrs("BlockItemTheftAbAttr", source, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled }); } if (cancelled.value) { @@ -2783,13 +2793,13 @@ export default class BattleScene extends SceneBase { if (target.isPlayer()) { this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant); if (source && itemLost) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: source }); } return true; } this.addEnemyModifier(newItemModifier, ignoreUpdate, instant); if (source && itemLost) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: source }); } return true; } @@ -2812,7 +2822,7 @@ export default class BattleScene extends SceneBase { const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { - applyAbAttrs("BlockItemTheftAbAttr", source, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled }); } if (cancelled.value) { @@ -3237,7 +3247,7 @@ export default class BattleScene extends SceneBase { (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) && achv.validate(args) ) { - this.gameData.achvUnlocks[achv.id] = new Date().getTime(); + this.gameData.achvUnlocks[achv.id] = Date.now(); this.ui.achvBar.showAchv(achv); if (vouchers.hasOwnProperty(achv.id)) { this.validateVoucher(vouchers[achv.id]); @@ -3250,7 +3260,7 @@ export default class BattleScene extends SceneBase { validateVoucher(voucher: Voucher, args?: unknown[]): boolean { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) { - this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); + this.gameData.voucherUnlocks[voucher.id] = Date.now(); this.ui.achvBar.showAchv(voucher); this.gameData.voucherCounts[voucher.voucherType]++; return true; diff --git a/src/battle.ts b/src/battle.ts index 245705f4801..45373402e12 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -178,7 +178,7 @@ export default class Battle { ) .map(i => { const ret = i as PokemonHeldItemModifier; - //@ts-ignore - this is awful to fix/change + //@ts-expect-error - this is awful to fix/change ret.pokemonId = null; return ret; }), diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index de200027ad8..416a8055d78 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -81,7 +81,12 @@ import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; import type { Constructor } from "#app/utils/common"; import type { Localizable } from "#app/@types/locales"; import { applyAbAttrs } from "./apply-ab-attrs"; +import type { Closed, Exact } from "#app/@types/type-helpers"; +// biome-ignore-start lint/correctness/noUnusedImports: Used in TSDoc +import type BattleScene from "#app/battle-scene"; +import type { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms/form-change-triggers"; +// biome-ignore-end lint/correctness/noUnusedImports: Used in TSDoc export class Ability implements Localizable { public id: AbilityId; @@ -135,7 +140,8 @@ export class Ability implements Localizable { if (!targetAttr) { return []; } - return this.attrs.filter((a): a is AbAttrMap[T] => a instanceof targetAttr); + // TODO: figure out how to remove the `as AbAttrMap[T][]` cast + return this.attrs.filter((a): a is AbAttrMap[T] => a instanceof targetAttr) as AbAttrMap[T][]; } /** @@ -220,6 +226,37 @@ export class Ability implements Localizable { } } +/** Base set of parameters passed to every ability attribute's apply method */ +export interface AbAttrBaseParams { + /** The pokemon that has the ability being applied */ + readonly pokemon: Pokemon; + + /** + * Whether the ability's effects are being simulated (for instance, during AI damage calculations). + * + * @remarks + * Used to prevent message flyouts and other effects from being triggered. + * @defaultValue `false` + */ + readonly simulated?: boolean; + + /** + * (For callers of {@linkcode applyAbAttrs}): If provided, **only** apply ability attributes of the passive (true) or active (false). + * + * This should almost always be left undefined, as otherwise it will *only* apply attributes of *either* the pokemon's passive (true) or + * non-passive (false) ability. In almost all cases, you want to apply attributes that are from either. + * + * (For implementations of {@linkcode AbAttr}): This will *never* be undefined, and will be `true` if the ability being applied + * is the pokemon's passive, and `false` otherwise. + */ + passive?: boolean; +} + +export interface AbAttrParamsWithCancel extends AbAttrBaseParams { + /** Whether the ability application results in the interaction being cancelled */ + readonly cancelled: BooleanHolder; +} + export abstract class AbAttr { public showAbility: boolean; private extraCondition: AbAttrCondition; @@ -250,25 +287,21 @@ export abstract class AbAttr { } /** - * Applies ability effects without checking conditions - * @param _pokemon - The pokemon to apply this ability to - * @param _passive - Whether or not the ability is a passive - * @param _simulated - Whether the call is simulated - * @param _args - Extra args passed to the function. Handled by child classes. - * @see {@linkcode canApply} + * Apply ability effects without checking conditions. + * **Never call this method directly, use {@linkcode applyAbAttrs} instead.** */ - apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void {} + apply(_params: AbAttrBaseParams): void {} - getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + // The `Exact` in the next two signatures enforces that the type of the _params operand + // is always compatible with the type of apply. This allows fewer fields, but never a type with more. + getTriggerMessage(_params: Exact[0]>, _abilityName: string): string | null { return null; } + canApply(_params: Exact[0]>): boolean { + return true; + } + getCondition(): AbAttrCondition | null { return this.extraCondition || null; } @@ -277,42 +310,22 @@ export abstract class AbAttr { this.extraCondition = condition; return this; } - - /** - * Returns a boolean describing whether the ability can be applied under current conditions - * @param _pokemon - The pokemon to apply this ability to - * @param _passive - Whether or not the ability is a passive - * @param _simulated - Whether the call is simulated - * @param _args - Extra args passed to the function. Handled by child classes. - * @returns `true` if the ability can be applied, `false` otherwise - * @see {@linkcode apply} - */ - canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - return true; - } } export class BlockRecoilDamageAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } +} - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { - return i18next.t("abilityTriggers:blockRecoilDamage", { - pokemonName: getPokemonNameWithAffix(pokemon), - abilityName: abilityName, - }); - } +export interface DoubleBattleChanceAbAttrParams extends AbAttrBaseParams { + /** Holder for the chance of a double battle that may be modified by the ability */ + chance: NumberHolder; } /** @@ -321,34 +334,23 @@ export class BlockRecoilDamageAttr extends AbAttr { * @see {@linkcode apply} */ export class DoubleBattleChanceAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } /** - * Increases the chance of a double battle occurring - * @param args [0] {@linkcode NumberHolder} for double battle chance + * Increase the chance of a double battle occurring, storing the result in `chance` */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const doubleBattleChance = args[0] as NumberHolder; - // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt - // A double battle will initiate if the generated number is 0 - doubleBattleChance.value = doubleBattleChance.value / 4; + override apply({ chance }: DoubleBattleChanceAbAttrParams): void { + // This is divided by 4 as the chance is generated as a number from 0 to chance.value using Utils.randSeedInt + // A double battle will initiate if the generated number is 0. + chance.value /= 4; } } export class PostBattleInitAbAttr extends AbAttr { - canApplyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args?: any[]): boolean { - return true; - } - - applyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args?: any[]): void {} + private declare readonly _: never; } export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { @@ -360,12 +362,12 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { this.formFunc = formFunc; } - override canApplyPostBattleInit(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: never[]): boolean { + override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex && !simulated; } - override applyPostBattleInit(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } @@ -381,13 +383,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { this.stages = stages; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { @@ -407,6 +403,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { * Clears a specified weather whenever this attribute is called. */ export class ClearWeatherAbAttr extends AbAttr { + // TODO: evaluate why this is a field and constructor parameter even though it is never checked private weather: WeatherType[]; /** @@ -418,17 +415,14 @@ export class ClearWeatherAbAttr extends AbAttr { this.weather = weather; } - public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + /** + * @param _params - No parameters are used for this attribute. + */ + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetWeather(WeatherType.NONE); } - public override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE, pokemon); } @@ -439,6 +433,7 @@ export class ClearWeatherAbAttr extends AbAttr { * Clears a specified terrain whenever this attribute is called. */ export class ClearTerrainAbAttr extends AbAttr { + // TODO: evaluate why this is a field and constructor parameter even though it is never checked private terrain: TerrainType[]; /** @@ -450,17 +445,11 @@ export class ClearTerrainAbAttr extends AbAttr { this.terrain = terrain; } - public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(TerrainType.NONE); } - public override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + public override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(TerrainType.NONE, true, pokemon); } @@ -469,58 +458,50 @@ export class ClearTerrainAbAttr extends AbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; -export class PreDefendAbAttr extends AbAttr { - canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { - return true; - } +/** + * Shared interface for AbAttrs that interact with a move that is being used by or against the user. + * + * Often extended by other interfaces to add more parameters. + * Used, e.g. by {@linkcode PreDefendAbAttr} and {@linkcode PostAttackAbAttr} + */ +export interface AugmentMoveInteractionAbAttrParams extends AbAttrBaseParams { + /** The move used by (or against, for defend attributes) the pokemon with the ability */ + move: Move; + /** The pokemon on the other side of the interaction */ + opponent: Pokemon; +} - applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - _args: any[], - ): void {} +/** + * Shared interface for parameters of several {@linkcode PreDefendAbAttr} ability attributes that modify damage. + */ +export interface PreDefendModifyDamageAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holder for the amount of damage that will be dealt by a move */ + damage: NumberHolder; +} + +/** + * Class for abilities that apply effects before the defending Pokemon takes damage. + * + * ⚠️ This attribute must not be called via `applyAbAttrs` as its subclasses violate the Liskov Substitution Principle. + */ +export abstract class PreDefendAbAttr extends AbAttr { + private declare readonly _: never; } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { + override canApply({ pokemon, damage }: PreDefendModifyDamageAbAttrParams): boolean { return ( pokemon.isFullHp() && // Checks if pokemon has wonder_guard (which forces 1hp) pokemon.getMaxHp() > 1 && // Damage >= hp - (args[0] as NumberHolder).value >= pokemon.hp + damage.value >= pokemon.hp && + // Cannot apply if the pokemon already has sturdy from some other source + !pokemon.getTag(BattlerTagType.STURDY) ); } - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PreDefendModifyDamageAbAttrParams): void { if (!simulated) { pokemon.addTag(BattlerTagType.STURDY, 1); } @@ -528,17 +509,11 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { + getTriggerMessage({ pokemon }: AbAttrBaseParams, abilityName: string) { return i18next.t("abilityTriggers:blockItemTheft", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -546,23 +521,22 @@ export class BlockItemTheftAbAttr extends AbAttr { } } +export interface StabBoostAbAttrParams extends AbAttrBaseParams { + /** Holds the resolved STAB multiplier after ability application */ + multiplier: NumberHolder; +} + export class StabBoostAbAttr extends AbAttr { constructor() { super(false); } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return (args[0] as NumberHolder).value > 1; + override canApply({ multiplier }: StabBoostAbAttrParams): boolean { + return multiplier.value > 1; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value += 0.5; + override apply({ multiplier }: StabBoostAbAttrParams): void { + multiplier.value += 0.5; } } @@ -577,28 +551,12 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { this.damageMultiplier = damageMultiplier; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PreDefendModifyDamageAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.damageMultiplier); + override apply({ damage }: PreDefendModifyDamageAbAttrParams): void { + damage.value = toDmgValue(damage.value * this.damageMultiplier); } } @@ -615,20 +573,9 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { } /** - * Handles the damage reduction - * @param args - * - `[0]` {@linkcode NumberHolder} - The damage being dealt + * Apply the damage reduction multiplier to the damage value. */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - const damage = args[0] as NumberHolder; + override apply({ damage }: PreDefendModifyDamageAbAttrParams): void { damage.value = toDmgValue(damage.value * this.damageMultiplier); } } @@ -639,9 +586,18 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip } } +/** + * Shared interface used by several {@linkcode PreDefendAbAttr} abilities that influence the computed type effectiveness + */ +export interface TypeMultiplierAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the type multiplier of an attack. In the case of an immunity, this value will be set to `0`. */ + typeMultiplier: NumberHolder; + /** Its particular meaning depends on the ability attribute, though usually means that the "no effect" message should not be played */ + cancelled: BooleanHolder; +} + /** * Determines whether a Pokemon is immune to a move because of an ability. - * @extends PreDefendAbAttr * @see {@linkcode applyPreDefend} * @see {@linkcode getCondition} */ @@ -657,15 +613,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { this.condition = condition ?? null; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: TypeMultiplierAbAttrParams): boolean { return ( ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) && attacker !== pokemon && @@ -673,26 +621,8 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { ); } - /** - * Applies immunity if this ability grants immunity to the type of the given move. - * @param _pokemon {@linkcode Pokemon} The defending Pokemon. - * @param _passive - Whether the ability is passive. - * @param _attacker {@linkcode Pokemon} The attacking Pokemon. - * @param _move {@linkcode Move} The attacking move. - * @param _cancelled {@linkcode BooleanHolder} - A holder for a boolean value indicating if the move was cancelled. - * @param args [0] {@linkcode NumberHolder} gets set to 0 if move is immuned by an ability. - * @param args [1] - Whether the move is simulated. - */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ typeMultiplier }: TypeMultiplierAbAttrParams): void { + typeMultiplier.value = 0; } getImmuneType(): PokemonType | null { @@ -710,39 +640,14 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { super(immuneType, condition); } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { + override canApply(params: TypeMultiplierAbAttrParams): boolean { + const { move } = params; return ( move.category !== MoveCategory.STATUS && !move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") && - super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args) + super.canApply(params) ); } - - /** - * Applies immunity if the move used is not a status move. - * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type - * Example: Levitate - */ - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - // this is a hacky way to fix the Levitate/Thousand Arrows interaction, but it works for now... - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } } export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { @@ -751,28 +656,9 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + super.apply(params); + const { pokemon, cancelled, simulated, passive } = params; if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -801,28 +687,9 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + const { cancelled, simulated, pokemon } = params; + super.apply(params); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { globalScene.phaseManager.unshiftNew( @@ -847,28 +714,9 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - } - - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: TypeMultiplierAbAttrParams): void { + const { cancelled, simulated, pokemon } = params; + super.apply(params); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); @@ -881,36 +729,16 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const modifierValue = - args.length > 0 - ? (args[0] as NumberHolder).value - : pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move); - return move.is("AttackMove") && modifierValue < 2; + override canApply({ move, typeMultiplier }: TypeMultiplierAbAttrParams): boolean { + return move.is("AttackMove") && typeMultiplier.value < 2; } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ typeMultiplier, cancelled }: TypeMultiplierAbAttrParams): void { cancelled.value = true; // Suppresses "No Effect" message - (args[0] as NumberHolder).value = 0; + typeMultiplier.value = 0; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: TypeMultiplierAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:nonSuperEffectiveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -924,16 +752,10 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { * @extends PreDefendAbAttr */ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const typeMultiplier = args[0]; + /** + * Allow application if the pokemon with the ability is at full hp and the mvoe is not fixed damage + */ + override canApply({ typeMultiplier, move, pokemon }: TypeMultiplierAbAttrParams): boolean { return ( typeMultiplier instanceof NumberHolder && !move?.hasAttr("FixedDamageAttr") && @@ -943,70 +765,27 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { } /** - * Reduces a type multiplier to 0.5 if the source is at full HP. - * @param pokemon {@linkcode Pokemon} the Pokemon with this ability - * @param _passive n/a - * @param _simulated n/a (this doesn't change game state) - * @param _attacker n/a - * @param _move {@linkcode Move} the move being used on the source - * @param _cancelled n/a - * @param args `[0]` a container for the move's current type effectiveness multiplier + * Reduce the type multiplier to 0.5 if the source is at full HP. */ - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): void { - const typeMultiplier = args[0]; + override apply({ typeMultiplier, pokemon }: TypeMultiplierAbAttrParams): void { typeMultiplier.value = 0.5; pokemon.turnData.moveEffectiveness = 0.5; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: TypeMultiplierAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:fullHpResistType", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); } } -export class PostDefendAbAttr extends AbAttr { - canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - return true; - } - - applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): void {} +export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds whether the pokemon is immune to the move being used */ + cancelled: BooleanHolder; } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean { return ( !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) && move.getPriority(attacker) > 0 && @@ -1014,41 +793,17 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { ); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: FieldPriorityMoveImmunityAbAttrParams): void { cancelled.value = true; } } -export class PostStatStageChangeAbAttr extends AbAttr { - canApplyPostStatStageChange( - _pokemon: Pokemon, - _simulated: boolean, - _statsChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): boolean { - return true; - } - - applyPostStatStageChange( - _pokemon: Pokemon, - _simulated: boolean, - _statsChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): void {} +export interface MoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds whether the standard "no effect" message (due to a type-based immunity) should be suppressed */ + cancelled: BooleanHolder; } - +// TODO: Consider examining whether this move immunity ability attribute +// can be merged with the MoveTypeMultiplierAbAttr in some way. export class MoveImmunityAbAttr extends PreDefendAbAttr { private immuneCondition: PreDefendAbAttrCondition; @@ -1058,70 +813,41 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: MoveImmunityAbAttrParams): boolean { + // TODO: Investigate whether this method should be checking against `cancelled`, specifically + // if not checking this results in multiple flyouts showing when multiple abilities block the move. return this.immuneCondition(pokemon, attacker, move); } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: MoveImmunityAbAttrParams): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: MoveImmunityAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); } } +export interface PreDefendModifyAccAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the accuracy of the move after the ability is applied */ + accuracy: NumberHolder; +} + /** * Reduces the accuracy of status moves used against the Pokémon with this ability to 50%. * Used by Wonder Skin. - * - * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { constructor() { super(false); } - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - const moveAccuracy = args[0] as NumberHolder; - return move.category === MoveCategory.STATUS && moveAccuracy.value >= 50; + override canApply({ move, accuracy }: PreDefendModifyAccAbAttrParams): boolean { + return move.category === MoveCategory.STATUS && accuracy.value >= 50; } - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - const moveAccuracy = args[0] as NumberHolder; - moveAccuracy.value = 50; + override apply({ accuracy }: PreDefendModifyAccAbAttrParams): void { + accuracy.value = 50; } } @@ -1135,52 +861,44 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return !simulated && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApply(params: MoveImmunityAbAttrParams): boolean { + // TODO: Evaluate whether it makes sense to check against simulated here. + // We likely want to check 'simulated' when the apply method enqueues the phase + return !params.simulated && super.canApply(params); } - override applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - cancelled: BooleanHolder, - args: any[], - ): void { - super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override apply(params: MoveImmunityAbAttrParams): void { + super.apply(params); + // TODO: We probably should not unshift the phase if this is simulated globalScene.phaseManager.unshiftNew( "StatStageChangePhase", - pokemon.getBattlerIndex(), + params.pokemon.getBattlerIndex(), true, [this.stat], this.stages, ); } } + /** - * Class for abilities that make drain moves deal damage to user instead of healing them. - * @extends PostDefendAbAttr - * @see {@linkcode applyPostDefend} + * Shared parameters for ability attributes that apply an effect after move was used by or against the the user. */ +export interface PostMoveInteractionAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Stores the hit result of the move used in the interaction */ + readonly hitResult: HitResult; +} + +export class PostDefendAbAttr extends AbAttr { + private declare readonly _: never; + override canApply(_params: PostMoveInteractionAbAttrParams): boolean { + return true; + } + override apply(_params: PostMoveInteractionAbAttrParams): void {} +} + +/** Class for abilities that make drain moves deal damage to user instead of healing them. */ export class ReverseDrainAbAttr extends PostDefendAbAttr { - override canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move }: PostMoveInteractionAbAttrParams): boolean { return move.hasAttr("HitHealAttr"); } @@ -1188,22 +906,8 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * Determines if a damage and draining move was used to check if this ability should stop the healing. * Examples include: Absorb, Draining Kiss, Bitter Blade, etc. * Also displays a message to show this ability was activated. - * @param _pokemon {@linkcode Pokemon} with this ability - * @param _passive N/A - * @param attacker {@linkcode Pokemon} that is attacking this Pokemon - * @param _move {@linkcode PokemonMove} that is being used - * @param _hitResult N/A - * @param _args N/A */ - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) }), @@ -1235,27 +939,11 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (simulated) { return; } @@ -1307,15 +995,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate); const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const damageReceived = lastAttackReceived?.damage || 0; @@ -1324,15 +1004,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1347,42 +1019,27 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { private condition: PokemonDefendCondition; - private tagType: ArenaTagType; + private arenaTagType: ArenaTagType; constructor(condition: PokemonDefendCondition, tagType: ArenaTagType) { super(true); this.condition = condition; - this.tagType = tagType; + this.arenaTagType = tagType; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - const tag = globalScene.arena.getTag(this.tagType) as ArenaTrapTag; + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { + const tag = globalScene.arena.getTag(this.arenaTagType) as ArenaTrapTag; return ( - this.condition(pokemon, attacker, move) && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) + this.condition(pokemon, attacker, move) && + (!globalScene.arena.getTag(this.arenaTagType) || tag.layers < tag.maxLayers) ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.addTag( - this.tagType, + this.arenaTagType, 0, undefined, pokemon.id, @@ -1402,27 +1059,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon, move }: PostMoveInteractionAbAttrParams): void { if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); globalScene.phaseManager.queueMessage( @@ -1438,34 +1079,24 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { private type: PokemonType; - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ + opponent: attacker, + move, + pokemon, + hitResult, + simulated, + }: PostMoveInteractionAbAttrParams): boolean { this.type = attacker.getMoveType(move); const pokemonTypes = pokemon.getTypes(true); return hitResult < HitResult.NO_EFFECT && (simulated || pokemonTypes.length !== 1 || pokemonTypes[0] !== this.type); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): void { const type = attacker.getMoveType(move); pokemon.summonData.types = [type]; } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1483,27 +1114,11 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - override canApplyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ hitResult }: PostMoveInteractionAbAttrParams): boolean { return hitResult < HitResult.NO_EFFECT && globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -1511,7 +1126,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { } export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { - public chance: number; + private chance: number; private effects: StatusEffect[]; constructor(chance: number, ...effects: StatusEffect[]) { @@ -1521,15 +1136,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, move, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; return ( @@ -1540,15 +1147,8 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { + // TODO: Probably want to check against simulated here const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; attacker.trySetStatus(effect, true, pokemon); @@ -1560,31 +1160,9 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - override canApplyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { - return ( - !(attacker.hasAbility(AbilityId.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) && - super.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args) - ); - } - - override applyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - args: any[], - ): void { - super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const attacker = params.opponent; + return !(attacker.isOfType(PokemonType.GRASS) || attacker.hasAbility(AbilityId.OVERCOAT)) && super.canApply(params); } } @@ -1601,15 +1179,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, pokemon, opponent: attacker }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && pokemon.randBattleSeedInt(100) < this.chance && @@ -1617,15 +1187,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, move }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); } @@ -1643,15 +1205,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1679,15 +1233,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ simulated, move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( !simulated && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && @@ -1695,20 +1241,12 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent: attacker }: PostMoveInteractionAbAttrParams): void { attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1731,37 +1269,21 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && !attacker.getTag(BattlerTagType.PERISH_SONG) ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); } } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName, @@ -1780,15 +1302,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent: attacker, move }: PostMoveInteractionAbAttrParams): boolean { return ( !(this.condition && !this.condition(pokemon, attacker, move)) && !globalScene.arena.weather?.isImmutable() && @@ -1796,15 +1310,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -1812,30 +1318,14 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { } export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && attacker.getAbility().isSwappable ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): void { if (!simulated) { const tempAbility = attacker.getAbility(); attacker.setTempAbility(pokemon.getAbility()); @@ -1843,7 +1333,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } } - override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); @@ -1858,15 +1348,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && attacker.getAbility().isSuppressable && @@ -1874,21 +1356,13 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker }: PostMoveInteractionAbAttrParams): void { if (!simulated) { attacker.setTempAbility(allAbilities[this.ability]); } } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PostMoveInteractionAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postDefendAbilityGive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -1907,15 +1381,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { + override canApply({ move, opponent: attacker, pokemon }: PostMoveInteractionAbAttrParams): boolean { return ( isNullOrUndefined(attacker.getTag(BattlerTagType.DISABLED)) && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && @@ -1923,15 +1389,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { ); } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated, opponent: attacker, move, pokemon }: PostMoveInteractionAbAttrParams): void { + // TODO: investigate why this is setting properties if (!simulated) { this.attacker = attacker; this.move = move; @@ -1940,6 +1399,25 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { } } +export interface PostStatStageChangeAbAttrParams extends AbAttrBaseParams { + /** The stats that were changed */ + stats: BattleStat[]; + /** The amount of stages that the stats changed by */ + stages: number; + /** Whether the source of the stat stages were from the user's own move */ + selfTarget: boolean; +} + +export class PostStatStageChangeAbAttr extends AbAttr { + private declare readonly _: never; + + override canApply(_params: Closed) { + return true; + } + + override apply(_params: Closed) {} +} + export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChangeAbAttr { private condition: PokemonStatStageChangeCondition; private statsToChange: BattleStat[]; @@ -1953,25 +1431,14 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang this.stages = stages; } - override canApplyPostStatStageChange( - pokemon: Pokemon, - _simulated: boolean, - statStagesChanged: BattleStat[], - stagesChanged: number, - selfTarget: boolean, - _args: any[], - ): boolean { - return this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget; + override canApply({ pokemon, stats, stages, selfTarget }: PostStatStageChangeAbAttrParams): boolean { + return this.condition(pokemon, stats, stages) && !selfTarget; } - override applyPostStatStageChange( - pokemon: Pokemon, - simulated: boolean, - _statStagesChanged: BattleStat[], - _stagesChanged: number, - _selfTarget: boolean, - _args: any[], - ): void { + /** + * Add additional stat changes when one of the pokemon's own stats change + */ + override apply({ simulated, pokemon }: PostStatStageChangeAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -1984,32 +1451,19 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang } } -export class PreAttackAbAttr extends AbAttr { - canApplyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): boolean { - return true; - } +export abstract class PreAttackAbAttr extends AbAttr { + private declare readonly _: never; +} - applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): void {} +export interface ModifyMoveEffectChanceAbAttrParams extends AbAttrBaseParams { + /** The move being used by the attacker */ + move: Move; + /** Holds the additional effect chance. Must be between `0` and `1` */ + chance: NumberHolder; } /** * Modifies moves additional effects with multipliers, ie. Sheer Force, Serene Grace. - * @extends AbAttr - * @see {@linkcode apply} */ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { private chanceMultiplier: number; @@ -2019,96 +1473,64 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { this.chanceMultiplier = chanceMultiplier; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + override canApply({ chance, move }: ModifyMoveEffectChanceAbAttrParams): boolean { const exceptMoves = [MoveId.ORDER_UP, MoveId.ELECTRO_SHOT]; - return !((args[0] as NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); + return !(chance.value <= 0 || exceptMoves.includes(move.id)); } - /** - * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. - * [1]: {@linkcode MoveId } Move used by the ability user. - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.chanceMultiplier; - (args[0] as NumberHolder).value = Math.min((args[0] as NumberHolder).value, 100); + override apply({ chance }: ModifyMoveEffectChanceAbAttrParams): void { + chance.value *= this.chanceMultiplier; + chance.value = Math.min(chance.value, 100); } } /** * Sets incoming moves additional effect chance to zero, ignoring all effects from moves. ie. Shield Dust. - * @extends PreDefendAbAttr - * @see {@linkcode applyPreDefend} */ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { constructor(showAbility = false) { super(showAbility); } - override canApplyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move | null, - _cancelled: BooleanHolder | null, - args: any[], - ): boolean { - return (args[0] as NumberHolder).value > 0; + override canApply({ chance }: ModifyMoveEffectChanceAbAttrParams): boolean { + return chance.value > 0; } - /** - * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. - */ - override applyPreDefend( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ chance }: ModifyMoveEffectChanceAbAttrParams): void { + chance.value = 0; } } -export class VariableMovePowerAbAttr extends PreAttackAbAttr { - override canApplyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - _args: any[], - ): boolean { - return true; - } +export interface FieldPreventExplosiveMovesAbAttrParams extends AbAttrBaseParams { + /** Holds whether the explosive move should be prevented*/ + cancelled: BooleanHolder; } export class FieldPreventExplosiveMovesAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + // TODO: investigate whether we need to check against `cancelled` in a `canApply` method + override apply({ cancelled }: FieldPreventExplosiveMovesAbAttrParams): void { cancelled.value = true; } } +export interface FieldMultiplyStatAbAttrParams extends AbAttrBaseParams { + /** The kind of stat that is being checked for modification */ + stat: Stat; + /** Holds the value of the stat after multipliers */ + statVal: NumberHolder; + /** The target of the stat multiplier */ + target: Pokemon; + /** Holds whether another multiplier has already been applied to the stat. + * + * @remarks + * Intended to be used to prevent the multiplier from stacking + * with other instances of the ability */ + hasApplied: BooleanHolder; +} + /** * Multiplies a Stat if the checked Pokemon lacks this ability. * If this ability cannot stack, a BooleanHolder can be used to prevent this from stacking. - * @see {@link applyFieldStatMultiplierAbAttrs} - * @see {@link applyFieldStat} - * @see {@link BooleanHolder} */ export class FieldMultiplyStatAbAttr extends AbAttr { private stat: Stat; @@ -2123,49 +1545,32 @@ export class FieldMultiplyStatAbAttr extends AbAttr { this.canStack = canStack; } - canApplyFieldStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: Stat, - _statValue: NumberHolder, - checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - _args: any[], - ): boolean { + canApply({ hasApplied, target, stat }: FieldMultiplyStatAbAttrParams): boolean { return ( this.canStack || (!hasApplied.value && this.stat === stat && - checkedPokemon.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat)) + target.getAbilityAttrs("FieldMultiplyStatAbAttr").every(attr => attr.stat !== stat)) ); } /** * applyFieldStat: Tries to multiply a Pokemon's Stat - * @param _pokemon {@linkcode Pokemon} the Pokemon using this ability - * @param _passive {@linkcode boolean} unused - * @param _stat {@linkcode Stat} the type of the checked stat - * @param statValue {@linkcode NumberHolder} the value of the checked stat - * @param _checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting - * @param hasApplied {@linkcode BooleanHolder} whether or not another multiplier has been applied to this stat - * @param _args {any[]} unused */ - applyFieldStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: Stat, - statValue: NumberHolder, - _checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - _args: any[], - ): void { - statValue.value *= this.multiplier; + apply({ statVal, hasApplied }: FieldMultiplyStatAbAttrParams): void { + statVal.value *= this.multiplier; hasApplied.value = true; } } +export interface MoveTypeChangeAbAttrParams extends AugmentMoveInteractionAbAttrParams { + // TODO: Replace the number holder with a holder for the type. + /** Holds the type of the move, which may change after ability application */ + moveType: NumberHolder; + /** Holds the power of the move, which may change after ability application */ + power: NumberHolder; +} + export class MoveTypeChangeAbAttr extends PreAttackAbAttr { constructor( private newType: PokemonType, @@ -2183,24 +1588,10 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { * - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode MoveId.MULTI_ATTACK} * - The user is not terastallized and using tera blast * - The user is not a terastallized terapagos with tera stellar using tera starstorm - * @param pokemon - The pokemon that has the move type changing ability and is using the attacking move - * @param _passive - Unused - * @param _simulated - Unused - * @param _defender - The pokemon being attacked (unused) - * @param move - The move being used - * @param _args - args[0] holds the type that the move is changed to, args[1] holds the multiplier - * @returns whether the move type change attribute can be applied */ - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: [NumberHolder?, NumberHolder?, ...any], - ): boolean { + override canApply({ pokemon, opponent: target, move }: MoveTypeChangeAbAttrParams): boolean { return ( - (!this.condition || this.condition(pokemon, _defender, move)) && + (!this.condition || this.condition(pokemon, target, move)) && !noAbilityTypeOverrideMoves.has(move.id) && (!pokemon.isTerastallized || (move.id !== MoveId.TERA_BLAST && @@ -2210,28 +1601,9 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { ); } - /** - * @param _pokemon - The pokemon that has the move type changing ability and is using the attacking move - * @param _passive - Unused - * @param _simulated - Unused - * @param _defender - The pokemon being attacked (unused) - * @param _move - The move being used - * @param args - args[0] holds the type that the move is changed to, args[1] holds the multiplier - */ - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: [NumberHolder?, NumberHolder?, ...any], - ): void { - if (args[0] && args[0] instanceof NumberHolder) { - args[0].value = this.newType; - } - if (args[1] && args[1] instanceof NumberHolder) { - args[1].value *= this.powerMultiplier; - } + override apply({ moveType, power }: MoveTypeChangeAbAttrParams): void { + moveType.value = this.newType; + power.value *= this.powerMultiplier; } } @@ -2245,14 +1617,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { super(true); } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { + override canApply({ move, pokemon }: AugmentMoveInteractionAbAttrParams): boolean { if ( pokemon.isTerastallized || move.id === MoveId.STRUGGLE || @@ -2276,14 +1641,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { return true; } - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _defender: Pokemon, - move: Move, - _args: any[], - ): void { + override apply({ simulated, pokemon, move }: AugmentMoveInteractionAbAttrParams): void { const moveType = pokemon.getMoveType(move); if (!simulated) { @@ -2293,7 +1651,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AugmentMoveInteractionAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveType: i18next.t(`pokemonInfo:Type.${PokemonType[this.moveType]}`), @@ -2301,6 +1659,16 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } } +/** + * Parameters for abilities that modify the hit count and damage of a move + */ +export interface AddSecondStrikeAbAttrParams extends Omit { + /** Holder for the number of hits. May be modified by ability application */ + hitCount?: NumberHolder; + /** Holder for the damage multiplier _of the current hit_ */ + multiplier?: NumberHolder; +} + /** * Class for abilities that convert single-strike moves to two-strike moves (i.e. Parental Bond). * @param damageMultiplier the damage multiplier for the second strike, relative to the first. @@ -2308,44 +1676,27 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { export class AddSecondStrikeAbAttr extends PreAttackAbAttr { private damageMultiplier: number; + /** + * @param damageMultiplier - The damage multiplier for the second strike, relative to the first + */ constructor(damageMultiplier: number) { super(false); this.damageMultiplier = damageMultiplier; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { + /** + * Return whether the move can be multi-strike enhanced + */ + override canApply({ pokemon, move }: AddSecondStrikeAbAttrParams): boolean { return move.canBeMultiStrikeEnhanced(pokemon, true); } /** - * If conditions are met, this doubles the move's hit count (via args[1]) - * or multiplies the damage of secondary strikes (via args[2]) - * @param pokemon the {@linkcode Pokemon} using the move - * @param _passive n/a - * @param _defender n/a - * @param _move the {@linkcode Move} used by the ability source - * @param args Additional arguments: - * - `[0]` the number of strikes this move currently has ({@linkcode NumberHolder}) - * - `[1]` the damage multiplier for the current strike ({@linkcode NumberHolder}) + * Add one to the move's hit count, and, if the pokemon has only one hit left, sets the damage multiplier + * to the damage multiplier of this ability. */ - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - const hitCount = args[0] as NumberHolder; - const multiplier = args[1] as NumberHolder; + override apply({ hitCount, multiplier, pokemon }: AddSecondStrikeAbAttrParams): void { if (hitCount?.value) { hitCount.value += 1; } @@ -2356,6 +1707,16 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { } } +/** + * Common interface for parameters used by abilities that modify damage/power of a move before an attack + */ +export interface PreAttackModifyDamageAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** + * The amount of damage dealt by the move. May be modified by ability application. + */ + damage: NumberHolder; +} + /** * Class for abilities that boost the damage of moves * For abilities that boost the base power of moves, see VariableMovePowerAbAttr @@ -2372,38 +1733,37 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { this.condition = condition; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { - return this.condition(pokemon, defender, move); + override canApply({ pokemon, opponent: target, move }: PreAttackModifyDamageAbAttrParams): boolean { + return this.condition(pokemon, target, move); } /** - * - * @param _pokemon the attacker pokemon - * @param _passive N/A - * @param _defender the target pokemon - * @param _move the move used by the attacker pokemon - * @param args Utils.NumberHolder as damage + * Adjust the power by the damage multiplier. */ - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - const power = args[0] as NumberHolder; + override apply({ damage: power }: PreAttackModifyDamageAbAttrParams): void { power.value = toDmgValue(power.value * this.damageMultiplier); } } +export interface PreAttackModifyPowerAbAttrParams extends AugmentMoveInteractionAbAttrParams { + /** Holds the base power of the move, which may be modified after ability application */ + power: NumberHolder; +} + +/* +This base class *is* allowed to be invoked directly by `applyAbAttrs`. +As such, we require that all subclasses have compatible `apply` parameters. +To do this, we use the `Closed` type. This ensures that any subclass of `VariableMovePowerAbAttr` +may not modify the type of apply's parameter to an interface that introduces new fields +or changes the type of existing fields. +*/ +export abstract class VariableMovePowerAbAttr extends PreAttackAbAttr { + override canApply(_params: Closed): boolean { + return true; + } + override apply(_params: Closed): void {} +} + export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { private condition: PokemonAttackCondition; private powerMultiplier: number; @@ -2414,26 +1774,12 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.powerMultiplier = powerMultiplier; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - _args: any[], - ): boolean { - return this.condition(pokemon, defender, move); + override canApply({ pokemon, opponent, move }: PreAttackModifyPowerAbAttrParams): boolean { + return this.condition(pokemon, opponent, move); } - override applyPreAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.powerMultiplier; + override apply({ power }: PreAttackModifyPowerAbAttrParams): void { + power.value *= this.powerMultiplier; } } @@ -2456,48 +1802,31 @@ export class LowHpMoveTypePowerBoostAbAttr extends MoveTypePowerBoostAbAttr { /** * Abilities which cause a variable amount of power increase. - * @extends VariableMovePowerAbAttr - * @see {@link applyPreAttack} */ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { private mult: (user: Pokemon, target: Pokemon, move: Move) => number; /** - * @param mult A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier. - * @param {boolean} showAbility Whether to show the ability when it activates. + * @param mult - A function which takes the user, target, and move, and returns the power multiplier. 1 means no multiplier. + * @param showAbility - Whether to show the ability when it activates. */ constructor(mult: (user: Pokemon, target: Pokemon, move: Move) => number, showAbility = true) { super(showAbility); this.mult = mult; } - override canApplyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - _args: any[], - ): boolean { - return this.mult(pokemon, defender, move) !== 1; + override canApply({ pokemon, opponent, move }: PreAttackModifyPowerAbAttrParams): boolean { + return this.mult(pokemon, opponent, move) !== 1; } - override applyPreAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - args: any[], - ): void { - const multiplier = this.mult(pokemon, defender, move); - (args[0] as NumberHolder).value *= multiplier; + override apply({ pokemon, opponent, move, power }: PreAttackModifyPowerAbAttrParams): void { + const multiplier = this.mult(pokemon, opponent, move); + power.value *= multiplier; } } /** * Boosts the power of a Pokémon's move under certain conditions. - * @extends AbAttr */ export class FieldMovePowerBoostAbAttr extends AbAttr { // TODO: Refactor this class? It extends from base AbAttr but has preAttack methods and gets called directly instead of going through applyAbAttrsInternal @@ -2514,34 +1843,19 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - canApplyPreAttack( - _pokemon: Pokemon | null, - _passive: boolean | null, - _simulated: boolean, - _defender: Pokemon | null, - _move: Move, - _args: any[], - ): boolean { + canApply(_params: PreAttackModifyPowerAbAttrParams): boolean { return true; // logic for this attr is handled in move.ts instead of normally } - applyPreAttack( - pokemon: Pokemon | null, - _passive: boolean | null, - _simulated: boolean, - defender: Pokemon | null, - move: Move, - args: any[], - ): void { - if (this.condition(pokemon, defender, move)) { - (args[0] as NumberHolder).value *= this.powerMultiplier; + apply({ pokemon, opponent, move, power }: PreAttackModifyPowerAbAttrParams): void { + if (this.condition(pokemon, opponent, move)) { + power.value *= this.powerMultiplier; } } } /** * Boosts the power of a specific type of move. - * @extends FieldMovePowerBoostAbAttr */ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostAbAttr { /** @@ -2579,9 +1893,25 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr } } +export interface StatMultiplierAbAttrParams extends AbAttrBaseParams { + /** The move being used by the user in the interaction*/ + move: Move; + /** The stat to determine modification for*/ + stat: BattleStat; + /** Holds the value of the stat, which may change after ability application. */ + statVal: NumberHolder; +} + export class StatMultiplierAbAttr extends AbAttr { + private declare readonly _: never; private stat: BattleStat; private multiplier: number; + /** + * Function determining if the stat multiplier is able to be applied to the move. + * + * @remarks + * Currently only used by Hustle. + */ private condition: PokemonAttackCondition | null; constructor(stat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { @@ -2592,77 +1922,26 @@ export class StatMultiplierAbAttr extends AbAttr { this.condition = condition ?? null; } - canApplyStatStage( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _statValue: NumberHolder, - args: any[], - ): boolean { - const move = args[0] as Move; + override canApply({ pokemon, move, stat }: StatMultiplierAbAttrParams): boolean { return stat === this.stat && (!this.condition || this.condition(pokemon, null, move)); } - applyStatStage( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - statValue: NumberHolder, - _args: any[], - ): void { - statValue.value *= this.multiplier; + override apply({ statVal }: StatMultiplierAbAttrParams): void { + statVal.value *= this.multiplier; } } -export class PostAttackAbAttr extends AbAttr { - private attackCondition: PokemonAttackCondition; - - /** The default attackCondition requires that the selected move is a damaging move */ - constructor( - attackCondition: PokemonAttackCondition = (_user, _target, move) => move.category !== MoveCategory.STATUS, - showAbility = true, - ) { - super(showAbility); - - this.attackCondition = attackCondition; - } - +export interface AllyStatMultiplierAbAttrParams extends StatMultiplierAbAttrParams { /** - * By default, this method checks that the move used is a damaging attack before - * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} - * for an example of an effect that does not require a damaging move. + * Whether abilities are being ignored during the interaction (e.g. due to a Mold-Breaker like effect). + * + * Note that some abilities that provide stat multipliers to allies apply their boosts regardless of this flag. */ - canApplyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - move: Move, - _hitResult: HitResult | null, - _args: any[], - ): boolean { - // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. - // If attackRequired is false, we always defer to the secondary requirements. - return this.attackCondition(pokemon, defender, move); - } - - applyPostAttack( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _defender: Pokemon, - _move: Move, - _hitResult: HitResult | null, - _args: any[], - ): void {} + ignoreAbility: boolean; } /** * Multiplies a Stat from an ally pokemon's ability. - * @see {@link applyAllyStatMultiplierAbAttrs} - * @see {@link applyAllyStat} */ export class AllyStatMultiplierAbAttr extends AbAttr { private stat: BattleStat; @@ -2684,65 +1963,29 @@ export class AllyStatMultiplierAbAttr extends AbAttr { /** * Multiply a Pokemon's Stat due to an Ally's ability. - * @param _pokemon - The ally {@linkcode Pokemon} with the ability (unused) - * @param passive - unused - * @param _simulated - Whether the ability is being simulated (unused) - * @param _stat - The type of the checked {@linkcode Stat} (unused) - * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param _checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) - * @param _ignoreAbility - Whether the ability should be ignored if possible - * @param _args - unused - * @returns `true` if this changed the checked stat, `false` otherwise. */ - applyAllyStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - statValue: NumberHolder, - _checkedPokemon: Pokemon, - _ignoreAbility: boolean, - _args: any[], - ) { - statValue.value *= this.multiplier; + apply({ statVal }: AllyStatMultiplierAbAttrParams) { + statVal.value *= this.multiplier; } /** - * Check if this ability can apply to the checked stat. - * @param _pokemon - The ally {@linkcode Pokemon} with the ability (unused) - * @param passive - unused - * @param _simulated - Whether the ability is being simulated (unused) - * @param stat - The type of the checked {@linkcode Stat} - * @param _statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param _checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) - * @param ignoreAbility - Whether the ability should be ignored if possible - * @param _args - unused - * @returns `true` if this can apply to the checked stat, `false` otherwise. + * @returns Whether the ability with this attribute can apply to the checked stat */ - canApplyAllyStat( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _statValue: NumberHolder, - _checkedPokemon: Pokemon, - ignoreAbility: boolean, - _args: any[], - ): boolean { + canApply({ stat, ignoreAbility }: AllyStatMultiplierAbAttrParams): boolean { return stat === this.stat && !(ignoreAbility && this.ignorable); } } /** - * Takes effect whenever a move succesfully executes, such as gorilla tactics' move-locking. + * Takes effect whenever the user's move succesfully executes, such as gorilla tactics' move-locking. * (More specifically, whenever a move is pushed to the move history) */ export class ExecutedMoveAbAttr extends AbAttr { - canApplyExecutedMove(_pokemon: Pokemon, _simulated: boolean): boolean { + canApply(_params: Closed): boolean { return true; } - applyExecutedMove(_pokemon: Pokemon, _simulated: boolean): void {} + apply(_params: Closed): void {} } /** @@ -2754,17 +1997,50 @@ export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr { super(showAbility); } - override canApplyExecutedMove(pokemon: Pokemon, _simulated: boolean): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { + // TODO: Consider whether checking against simulated makes sense here return !pokemon.getTag(BattlerTagType.GORILLA_TACTICS); } - override applyExecutedMove(pokemon: Pokemon, simulated: boolean): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.addTag(BattlerTagType.GORILLA_TACTICS); } } } +/* +Subclasses that override the `canApply` and `apply` are not allowed to change the type of their parameters. +This is enforced via the `Closed` type. +*/ +/** + * Base class for abilities that apply some effect after the user's move successfully executes. + */ +export abstract class PostAttackAbAttr extends AbAttr { + private attackCondition: PokemonAttackCondition; + + /** The default `attackCondition` requires that the selected move is a damaging move */ + constructor( + attackCondition: PokemonAttackCondition = (_user, _target, move) => move.category !== MoveCategory.STATUS, + showAbility = true, + ) { + super(showAbility); + + this.attackCondition = attackCondition; + } + + /** + * By default, this method checks that the move used is a damaging attack. + * This can be changed by providing a different {@link attackCondition} to the constructor. + * @see {@link ConfusionOnStatusEffectAbAttr} for an example of an effect that does not require a damaging move. + */ + override canApply({ pokemon, opponent, move }: Closed): boolean { + return this.attackCondition(pokemon, opponent, move); + } + + override apply(_params: Closed): void {} +} + export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { private stealCondition: PokemonAttackCondition | null; private stolenItem?: PokemonHeldItemModifier; @@ -2775,22 +2051,18 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stealCondition = stealCondition ?? null; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { simulated, pokemon, opponent, move, hitResult } = params; + // TODO: Revisit the hitResult check here. + // The PostAttackAbAttr should should only be invoked in cases where the move successfully connected, + // calling `super.canApply` already checks that the move was a damage move and not a status move. if ( - super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && + super.canApply(params) && !simulated && hitResult < HitResult.NO_EFFECT && - (!this.stealCondition || this.stealCondition(pokemon, defender, move)) + (!this.stealCondition || this.stealCondition(pokemon, opponent, move)) ) { - const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (heldItems.length) { // Ensure that the stolen item in testing is the same as when the effect is applied this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; @@ -2803,16 +2075,8 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { return false; } - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - defender: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { - const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); + override apply({ opponent, pokemon }: PostMoveInteractionAbAttrParams): void { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -2820,7 +2084,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postAttackStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - defenderName: defender.name, + defenderName: opponent.name, stolenItemType: this.stolenItem.type.name, }), ); @@ -2849,45 +2113,30 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { simulated, pokemon, move, opponent } = params; if ( - super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && + super.canApply(params) && (simulated || - (!attacker.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && - pokemon !== attacker && + (!opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && + pokemon !== opponent && (!this.contactRequired || - move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: pokemon, target: opponent })) && pokemon.randBattleSeedInt(100) < this.chance && !pokemon.status)) ) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - return simulated || attacker.canSetStatus(effect, true, false, pokemon); + return simulated || opponent.canSetStatus(effect, true, false, pokemon); } return false; } - applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + apply({ pokemon, opponent }: PostMoveInteractionAbAttrParams): void { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - attacker.trySetStatus(effect, true, pokemon); + opponent.trySetStatus(effect, true, pokemon); } } @@ -2914,40 +2163,25 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { + override canApply(params: PostMoveInteractionAbAttrParams): boolean { + const { pokemon, move, opponent } = params; /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ return ( - super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && - !attacker.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && - pokemon !== attacker && + super.canApply(params) && + !opponent.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && + pokemon !== opponent && (!this.contactRequired || - move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && - pokemon.randBattleSeedInt(100) < this.chance(attacker, pokemon, move) && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: opponent, target: pokemon })) && + pokemon.randBattleSeedInt(100) < this.chance(opponent, pokemon, move) && !pokemon.status ); } - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon, simulated, opponent }: PostMoveInteractionAbAttrParams): void { if (!simulated) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - attacker.addTag(effect); + opponent.addTag(effect); } } } @@ -2962,17 +2196,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { - if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { - const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); + override canApply({ simulated, pokemon, opponent, move, hitResult }: PostMoveInteractionAbAttrParams): boolean { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, opponent, move))) { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (heldItems.length) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { @@ -2983,16 +2209,8 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { return false; } - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { - const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); + override apply({ pokemon, opponent }: PostMoveInteractionAbAttrParams): void { + const heldItems = this.getTargetHeldItems(opponent).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -3000,7 +2218,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - attackerName: attacker.name, + attackerName: opponent.name, stolenItemType: this.stolenItem.type.name, }), ); @@ -3016,38 +2234,29 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { } } +/** + * Shared parameters used for abilities that apply an effect after the user is inflicted with a status condition. + */ +export interface PostSetStatusAbAttrParams extends AbAttrBaseParams { + /** The pokemon that set the status condition, or `undefined` if not set by a pokemon */ + sourcePokemon?: Pokemon; + /** The status effect that was set */ + effect: StatusEffect; +} + +/* +Subclasses that override the `canApply` and `apply` methods of `PostSetStatusAbAttr` are not allowed to change the +type of their parameters. This is enforced via the Closed type. +*/ /** * Base class for defining all {@linkcode Ability} Attributes after a status effect has been set. - * @see {@linkcode applyPostSetStatus()}. */ export class PostSetStatusAbAttr extends AbAttr { - canApplyPostSetStatus( - _pokemon: Pokemon, - _sourcePokemon: Pokemon | null = null, - _passive: boolean, - _effect: StatusEffect, - _simulated: boolean, - _rgs: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - /** - * Does nothing after a status condition is set. - * @param _pokemon {@linkcode Pokemon} that status condition was set on. - * @param _sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon. - * @param _passive Whether this ability is a passive. - * @param _effect {@linkcode StatusEffect} that was set. - * @param _args Set of unique arguments needed by this attribute. - */ - applyPostSetStatus( - _pokemon: Pokemon, - _sourcePokemon: Pokemon | null = null, - _passive: boolean, - _effect: StatusEffect, - _simulated: boolean, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -3056,14 +2265,14 @@ export class PostSetStatusAbAttr extends AbAttr { * ability attribute. For Synchronize ability. */ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { - override canApplyPostSetStatus( - _pokemon: Pokemon, - sourcePokemon: (Pokemon | null) | undefined, - _passive: boolean, - effect: StatusEffect, - _simulated: boolean, - _args: any[], - ): boolean { + /** + * @returns Whether the status effect that was set is one of the synchronizable statuses: + * - {@linkcode StatusEffect.BURN | Burn} + * - {@linkcode StatusEffect.PARALYSIS | Paralysis} + * - {@linkcode StatusEffect.POISON | Poison} + * - {@linkcode StatusEffect.TOXIC | Toxic} + */ + override canApply({ sourcePokemon, effect }: PostSetStatusAbAttrParams): boolean { /** Synchronizable statuses */ const syncStatuses = new Set([ StatusEffect.BURN, @@ -3079,32 +2288,25 @@ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { /** * If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status * was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`. - * @param pokemon {@linkcode Pokemon} that status condition was set on. - * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon. - * @param _passive Whether this ability is a passive. - * @param effect {@linkcode StatusEffect} that was set. - * @param _args Set of unique arguments needed by this attribute. */ - override applyPostSetStatus( - pokemon: Pokemon, - sourcePokemon: Pokemon | null = null, - _passive: boolean, - effect: StatusEffect, - simulated: boolean, - _args: any[], - ): void { + override apply({ simulated, effect, sourcePokemon, pokemon }: PostSetStatusAbAttrParams): void { if (!simulated && sourcePokemon) { sourcePokemon.trySetStatus(effect, true, pokemon); } } } +/** + * Base class for abilities that apply an effect after the user knocks out an opponent in battle. + * + * Not to be confused with {@linkcode PostKnockOutAbAttr}, which applies after any pokemon is knocked out in battle. + */ export class PostVictoryAbAttr extends AbAttr { - canApplyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { @@ -3118,7 +2320,7 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { this.stages = stages; } - override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); @@ -3135,36 +2337,37 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { this.formFunc = formFunc; } - override canApplyPostVictory(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex; } - override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } } -export class PostKnockOutAbAttr extends AbAttr { - canApplyPostKnockOut( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): boolean { +/** + * Shared parameters used for abilities that apply an effect after a Pokemon (other than the user) is knocked out. + */ +export interface PostKnockOutAbAttrParams extends AbAttrBaseParams { + /** The Pokemon that was knocked out */ + victim: Pokemon; +} + +/** + * Base class for ability attributes that apply after a Pokemon (other than the user) is knocked out, including indirectly. + * + * Not to be confused with {@linkcode PostVictoryAbAttr}, which applies after the user directly knocks out an opponent. + */ +export abstract class PostKnockOutAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostKnockOut( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): void {} + apply(_params: Closed): void {} } export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { @@ -3178,13 +2381,7 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { this.stages = stages; } - override applyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _knockedOut: Pokemon, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PostKnockOutAbAttrParams): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); @@ -3193,35 +2390,29 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { } export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { - override canApplyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - knockedOut: Pokemon, - _args: any[], - ): boolean { - return pokemon.isPlayer() === knockedOut.isPlayer() && knockedOut.getAbility().isCopiable; + override canApply({ pokemon, victim }: PostKnockOutAbAttrParams): boolean { + return pokemon.isPlayer() === victim.isPlayer() && victim.getAbility().isCopiable; } - override applyPostKnockOut( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - knockedOut: Pokemon, - _args: any[], - ): void { + override apply({ pokemon, simulated, victim }: PostKnockOutAbAttrParams): void { if (!simulated) { - pokemon.setTempAbility(knockedOut.getAbility()); + pokemon.setTempAbility(victim.getAbility()); globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:copyFaintedAllyAbility", { - pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), - abilityName: allAbilities[knockedOut.getAbility().id].name, + pokemonNameWithAffix: getPokemonNameWithAffix(victim), + abilityName: allAbilities[victim.getAbility().id].name, }), ); } } } +export interface IgnoreOpponentStatStagesAbAttrParams extends AbAttrBaseParams { + /** The stat to check for ignorability */ + stat: BattleStat; + /** Holds whether the stat is ignored by the ability */ + ignored: BooleanHolder; +} /** * Ability attribute for ignoring the opponent's stat changes * @param stats the stats that should be ignored @@ -3235,45 +2426,35 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { this.stats = stats ?? BATTLE_STATS; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.stats.includes(args[0]); + /** + * @returns Whether `stat` is one of the stats ignored by the ability + */ + override canApply({ stat }: IgnoreOpponentStatStagesAbAttrParams): boolean { + return this.stats.includes(stat); } /** - * Modifies a BooleanHolder and returns the result to see if a stat is ignored or not - * @param _pokemon n/a - * @param _passive n/a - * @param _simulated n/a - * @param _cancelled n/a - * @param args A BooleanHolder that represents whether or not to ignore a stat's stat changes + * Sets the ignored holder to true. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[1] as BooleanHolder).value = true; + override apply({ ignored }: IgnoreOpponentStatStagesAbAttrParams): void { + ignored.value = true; } } +/** + * Abilities with this attribute prevent the user from being affected by Intimidate. + * @sealed + */ export class IntimidateImmunityAbAttr extends AbAttr { constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AbAttrParamsWithCancel, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:intimidateImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -3293,13 +2474,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon, simulated, cancelled }: AbAttrParamsWithCancel): void { if (!simulated) { globalScene.phaseManager.pushNew( "StatStageChangePhase", @@ -3317,7 +2492,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { * Base class for defining all {@linkcode Ability} Attributes post summon * @see {@linkcode applyPostSummon()} */ -export class PostSummonAbAttr extends AbAttr { +export abstract class PostSummonAbAttr extends AbAttr { /** Should the ability activate when gained in battle? This will almost always be true */ private activateOnGain: boolean; @@ -3333,23 +2508,20 @@ export class PostSummonAbAttr extends AbAttr { return this.activateOnGain; } - canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } /** * Applies ability post summon (after switching in) - * @param _pokemon {@linkcode Pokemon} with this ability - * @param _passive Whether this ability is a passive - * @param _args Set of unique arguments needed by this attribute */ - applyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Base class for ability attributes which remove an effect on summon */ -export class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {} +export abstract class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {} /** * Removes specified arena tags when a Pokemon is summoned. @@ -3366,11 +2538,11 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.tags.some(tag => this.arenaTags.includes(tag.tagType)); } - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { for (const arenaTag of this.arenaTags) { globalScene.arena.removeTag(arenaTag); @@ -3397,7 +2569,7 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr { this.quiet = quiet; } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + public override apply({ pokemon, simulated }: AbAttrBaseParams): void { this.sourceId = pokemon.id; if (!simulated) { globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet); @@ -3414,7 +2586,7 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.messageFunc(pokemon)); } @@ -3431,7 +2603,7 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.message); } @@ -3449,11 +2621,11 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.canAddTag(this.tagType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -3476,11 +2648,11 @@ export class PostSummonRemoveBattlerTagAbAttr extends PostSummonRemoveEffectAbAt this.immuneTags = immuneTags; } - public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + public override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.immuneTags.some(tagType => !!pokemon.getTag(tagType)); } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply({ pokemon }: AbAttrBaseParams): void { this.immuneTags.forEach(tagType => pokemon.removeTag(tagType)); } } @@ -3500,7 +2672,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (simulated) { return; } @@ -3515,27 +2687,29 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.stats, this.stages, ); - } else { - for (const opponent of pokemon.getOpponents()) { - const cancelled = new BooleanHolder(false); - if (this.intimidate) { - applyAbAttrs("IntimidateImmunityAbAttr", opponent, cancelled, simulated); - applyAbAttrs("PostIntimidateStatStageChangeAbAttr", opponent, cancelled, simulated); + return; + } - if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { - cancelled.value = true; - } - } - if (!cancelled.value) { - globalScene.phaseManager.unshiftNew( - "StatStageChangePhase", - opponent.getBattlerIndex(), - false, - this.stats, - this.stages, - ); + for (const opponent of pokemon.getOpponents()) { + const cancelled = new BooleanHolder(false); + if (this.intimidate) { + const params: AbAttrParamsWithCancel = { pokemon: opponent, cancelled, simulated }; + applyAbAttrs("IntimidateImmunityAbAttr", params); + applyAbAttrs("PostIntimidateStatStageChangeAbAttr", params); + + if (opponent.getTag(BattlerTagType.SUBSTITUTE)) { + cancelled.value = true; } } + if (!cancelled.value) { + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + opponent.getBattlerIndex(), + false, + this.stats, + this.stages, + ); + } } } } @@ -3551,11 +2725,11 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { globalScene.phaseManager.unshiftNew( @@ -3582,11 +2756,11 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { * @returns if the move was successful */ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { for (const s of BATTLE_STATS) { @@ -3606,8 +2780,6 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { * Download raises either the Attack stat or Special Attack stat by one stage depending on the foe's currently lowest defensive stat: * it will raise Attack if the foe's current Defense is lower than its current Special Defense stat; * otherwise, it will raise Special Attack. - * @extends PostSummonAbAttr - * @see {applyPostSummon} */ export class DownloadAbAttr extends PostSummonAbAttr { private enemyDef: number; @@ -3615,7 +2787,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { private enemyCountTally: number; private stats: BattleStat[]; - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -3633,11 +2805,8 @@ export class DownloadAbAttr extends PostSummonAbAttr { /** * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account * vitamins and items, so it needs to use the Stat and the stat alone. - * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. - * @param {boolean} _passive N/A - * @param {any[]} _args N/A */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (this.enemyDef < this.enemySpDef) { this.stats = [Stat.ATK]; } else { @@ -3659,7 +2828,7 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { const weatherReplaceable = this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || @@ -3668,7 +2837,7 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -3684,11 +2853,11 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -3710,12 +2879,13 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { this.immuneEffects = immuneEffects; } - public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + public override canApply({ pokemon }: AbAttrBaseParams): boolean { const status = pokemon.status?.effect; return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status)); } - public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply({ pokemon }: AbAttrBaseParams): void { + // TODO: should probably check against simulated... const status = pokemon.status?.effect; if (!isNullOrUndefined(status)) { this.statusHealed = status; @@ -3724,9 +2894,9 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { } } - public override getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + public override getTriggerMessage({ pokemon }: AbAttrBaseParams): string | null { if (this.statusHealed) { - return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(_pokemon)); + return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(pokemon)); } return null; } @@ -3741,11 +2911,11 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { this.formFunc = formFunc; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -3757,7 +2927,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -3783,7 +2953,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return true; } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.setTempAbility(this.target!.getAbility()); setAbilityRevealed(this.target!); @@ -3791,7 +2961,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }, _abilityName: string): string { return i18next.t("abilityTriggers:trace", { pokemonName: getPokemonNameWithAffix(pokemon), targetName: getPokemonNameWithAffix(this.target), @@ -3815,31 +2985,28 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt this.statusEffect = statusEffect; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); return party.filter(p => p.isAllowedInBattle()).length > 0; } /** * Removes supplied status effect from the user's field when user of the ability is summoned. - * - * @param pokemon - The Pokémon that triggered the ability. - * @param _passive - n/a - * @param _args - n/a */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); - if (!simulated) { - for (const pokemon of allowedParty) { - if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - globalScene.phaseManager.queueMessage( - getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), - ); - pokemon.resetStatus(false); - pokemon.updateInfo(); - } + for (const pokemon of allowedParty) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), + ); + pokemon.resetStatus(false); + pokemon.updateInfo(); } } } @@ -3847,7 +3014,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (!globalScene.currentBattle.double) { return false; } @@ -3856,9 +3023,12 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return !(isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const ally = pokemon.getAlly(); - if (!simulated && !isNullOrUndefined(ally)) { + if (!isNullOrUndefined(ally)) { for (const s of BATTLE_STATS) { pokemon.setStatStage(s, ally.getStatStage(s)); } @@ -3866,7 +3036,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: AbAttrBaseParams, _abilityName: string): string { return i18next.t("abilityTriggers:costar", { pokemonName: getPokemonNameWithAffix(pokemon), allyName: getPokemonNameWithAffix(pokemon.getAlly()), @@ -3907,7 +3077,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return target; } - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean { const targets = pokemon.getOpponents(); const target = this.getTarget(targets); @@ -3915,15 +3085,16 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return false; } + // TODO: Consider moving the simulated check to the apply method if (simulated || !targets.length) { - return simulated; + return !!simulated; } // transforming from or into fusion pokemon causes various problems (including crashes and save corruption) return !(this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { const target = this.getTarget(pokemon.getOpponents()); globalScene.phaseManager.unshiftNew( @@ -3941,17 +3112,14 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { * @extends PostSummonAbAttr */ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} - * @param {Pokemon} _pokemon the Pokemon with this ability - * @param _passive n/a - * @param _args n/a */ - override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChangesToNormal(); } @@ -3974,28 +3142,20 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { /** * Determine if the pokemon has a forme change that is triggered by the weather - * - * @param pokemon - The pokemon with the forme change ability - * @param _passive - unused - * @param _simulated - unused - * @param _args - unused */ - override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !!pokemonFormChanges[pokemon.species.speciesId]?.some( fc => fc.findTrigger(SpeciesFormChangeWeatherTrigger) && fc.canChange(pokemon), ); } /** - * Trigger the pokemon's forme change by invoking - * {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} - * - * @param pokemon - The Pokemon with this ability - * @param _passive - unused - * @param simulated - unused - * @param _args - unused + * Calls the {@linkcode BattleScene.triggerPokemonFormChange | triggerPokemonFormChange} for both + * {@linkcode SpeciesFormChangeWeatherTrigger} and + * {@linkcode SpeciesFormChangeRevertWeatherFormTrigger} if it + * is the specific Pokemon and ability */ - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger); } @@ -4013,7 +3173,7 @@ export class CommanderAbAttr extends AbAttr { super(true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { // If the ally Dondozo is fainted or was previously "commanded" by // another Pokemon, this effect cannot apply. @@ -4027,7 +3187,7 @@ export class CommanderAbAttr extends AbAttr { ); } - override apply(pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: null, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { // Lapse the source's semi-invulnerable tags (to avoid visual inconsistencies) pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); @@ -4041,28 +3201,34 @@ export class CommanderAbAttr extends AbAttr { } } -export class PreSwitchOutAbAttr extends AbAttr { +/** + * Base class for ability attributes that apply their effect when their user switches out. + */ +export abstract class PreSwitchOutAbAttr extends AbAttr { constructor(showAbility = true) { super(showAbility); } - canApplyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } +/** + * Resets all status effects on the user when it switches out. + */ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { constructor() { super(false); } - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !isNullOrUndefined(pokemon.status); } - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { pokemon.resetStatus(); pokemon.updateInfo(); @@ -4074,13 +3240,8 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { - /** - * @param pokemon The {@linkcode Pokemon} with the ability - * @param _passive N/A - * @param _args N/A - * @returns {boolean} Returns true if the weather clears, otherwise false. - */ - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override apply({ pokemon, simulated }: AbAttrBaseParams): boolean { + // TODO: Evaluate why this is returning a boolean rather than relay const weatherType = globalScene.arena.weather?.weatherType; let turnOffWeather = false; @@ -4135,11 +3296,11 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isFullHp(); } - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { if (!simulated) { const healAmount = toDmgValue(pokemon.getMaxHp() * 0.33); pokemon.heal(healAmount); @@ -4150,7 +3311,6 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { /** * Attribute for form changes that occur on switching out - * @extends PreSwitchOutAbAttr * @see {@linkcode applyPreSwitchOut} */ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { @@ -4162,36 +3322,36 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { this.formFunc = formFunc; } - override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } /** * On switch out, trigger the form change to the one defined in the ability - * @param pokemon The pokemon switching out and changing form {@linkcode Pokemon} - * @param _passive N/A - * @param _args N/A */ - override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } } +/** + * Base class for ability attributes that apply their effect just before the user leaves the field + */ export class PreLeaveFieldAbAttr extends AbAttr { - canApplyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { - override canApplyPreLeaveField(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const weatherType = globalScene.arena.weather?.weatherType; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. switch (weatherType) { @@ -4232,12 +3392,7 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { return false; } - /** - * @param _pokemon The {@linkcode Pokemon} with the ability - * @param _passive N/A - * @param _args N/A - */ - override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE); } @@ -4246,47 +3401,49 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { /** * Updates the active {@linkcode SuppressAbilitiesTag} when a pokemon with {@linkcode AbilityId.NEUTRALIZING_GAS} leaves the field + * + * @sealed */ export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFieldAbAttr { constructor() { super(false); } - public override canApplyPreLeaveField( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _args: any[], - ): boolean { + public override canApply(_params: AbAttrBaseParams): boolean { return !!globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS); } - public override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + public override apply(_params: AbAttrBaseParams): void { const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; suppressTag.onSourceLeave(globalScene.arena); } } -export class PreStatStageChangeAbAttr extends AbAttr { - canApplyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { +export interface PreStatStageChangeAbAttrParams extends AbAttrBaseParams { + /** The stat being affected by the stat stage change */ + stat: BattleStat; + /** The amount of stages to change by (negative if the stat is being decreased) */ + stages: number; + /** + * The source of the stat stage drop. May be omitted if the source of the stat drop is the user itself. + * + * @remarks + * Currently, only used by {@linkcode ReflectStatStageChangeAbAttr} in order to reflect the stat stage change + */ + source?: Pokemon; + /** Holder that will be set to true if the stat stage change should be cancelled due to the ability */ + cancelled: BooleanHolder; +} + +/** + * Base class for ability attributes that apply their effect before a stat stage change. + */ +export abstract class PreStatStageChangeAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -4297,30 +3454,22 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { /** {@linkcode BattleStat} to reflect */ private reflectedStat?: BattleStat; + override canApply({ source, cancelled }: PreStatStageChangeAbAttrParams): boolean { + return !!source && !cancelled.value; + } + /** * Apply the {@linkcode ReflectStatStageChangeAbAttr} to an interaction - * @param _pokemon The user pokemon - * @param _passive N/A - * @param simulated `true` if the ability is being simulated by the AI - * @param stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode BooleanHolder} that will be set to true due to reflection - * @param args */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: any[], - ): void { - const attacker: Pokemon = args[0]; - const stages = args[1]; + override apply({ source, cancelled, stat, simulated, stages }: PreStatStageChangeAbAttrParams): void { + if (!source) { + return; + } this.reflectedStat = stat; if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", - attacker.getBattlerIndex(), + source.getBattlerIndex(), false, [stat], stages, @@ -4334,7 +3483,7 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: PreStatStageChangeAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -4356,38 +3505,18 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } - override canApplyPreStatStageChange( - _pokemon: Pokemon | null, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ stat }: PreStatStageChangeAbAttrParams): boolean { return isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; } /** * Apply the {@linkcode ProtectedStatAbAttr} to an interaction - * @param _pokemon - * @param _passive - * @param simulated - * @param _stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode BooleanHolder} that will be set to true if the stat is protected - * @param _args */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreStatStageChangeAbAttrParams): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PreStatStageChangeAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -4396,85 +3525,60 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { } } +export interface ConfusionOnStatusEffectAbAttrParams extends AbAttrBaseParams { + /** The status effect that was applied */ + effect: StatusEffect; + /** The move that applied the status effect */ + move: Move; + /** The opponent that was inflicted with the status effect */ + opponent: Pokemon; +} + /** * This attribute applies confusion to the target whenever the user * directly poisons them with a move, e.g. Poison Puppeteer. * Called in {@linkcode StatusEffectAttr}. - * @extends PostAttackAbAttr - * @see {@linkcode applyPostAttack} */ -export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { +export class ConfusionOnStatusEffectAbAttr extends AbAttr { /** List of effects to apply confusion after */ private effects: StatusEffect[]; constructor(...effects: StatusEffect[]) { - /** This effect does not require a damaging move */ - super((_user, _target, _move) => true); + super(); this.effects = effects; } - override canApplyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], - ): boolean { - return ( - super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && - this.effects.indexOf(args[0]) > -1 && - !defender.isFainted() && - defender.canAddTag(BattlerTagType.CONFUSED) - ); + /** + * @returns Whether the ability can apply confusion to the opponent + */ + override canApply({ opponent, effect }: ConfusionOnStatusEffectAbAttrParams): boolean { + return this.effects.includes(effect) && !opponent.isFainted() && opponent.canAddTag(BattlerTagType.CONFUSED); } /** * Applies confusion to the target pokemon. - * @param pokemon {@link Pokemon} attacking - * @param _passive N/A - * @param defender {@link Pokemon} defending - * @param move {@link Move} used to apply status effect and confusion - * @param _hitResult N/A - * @param _args [0] {@linkcode StatusEffect} applied by move */ - override applyPostAttack( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ opponent, simulated, pokemon, move }: ConfusionOnStatusEffectAbAttrParams): void { if (!simulated) { - defender.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, defender.id); + opponent.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, opponent.id); } } } +export interface PreSetStatusAbAttrParams extends AbAttrBaseParams { + /** The status effect being applied */ + effect: StatusEffect; + /** Holds whether the status effect is prevented by the ability */ + cancelled: BooleanHolder; +} + export class PreSetStatusAbAttr extends AbAttr { /** Return whether the ability attribute can be applied */ - canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _effect: StatusEffect | undefined, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _effect: StatusEffect | undefined, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -4482,7 +3586,6 @@ export class PreSetStatusAbAttr extends AbAttr { */ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { protected immuneEffects: StatusEffect[]; - private lastEffect: StatusEffect; /** * @param immuneEffects - The status effects to which the Pokémon is immune. @@ -4493,44 +3596,23 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { this.immuneEffects = immuneEffects; } - override canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ effect }: PreSetStatusAbAttrParams): boolean { return (effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || this.immuneEffects.includes(effect); } /** * Applies immunity to supplied status effects. - * - * @param _pokemon - The Pokémon to which the status is being applied. - * @param _passive - n/a - * @param effect - The status effect being applied. - * @param cancelled - A holder for a boolean value indicating if the status application was cancelled. - * @param _args - n/a */ - override applyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreSetStatusAbAttrParams): void { cancelled.value = true; - this.lastEffect = effect; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon, effect }: PreSetStatusAbAttrParams, abilityName: string): string { return this.immuneEffects.length ? i18next.t("abilityTriggers:statusEffectImmunityWithName", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statusEffectName: getStatusEffectDescriptor(this.lastEffect), + statusEffectName: getStatusEffectDescriptor(effect), }) : i18next.t("abilityTriggers:statusEffectImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), @@ -4539,63 +3621,98 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { } } +// NOTE: There is a good amount of overlapping code between this +// and PreSetStatusEffectImmunity. However, we need these classes to be distinct +// as this one's apply method requires additional parameters +// TODO: Find away to avoid the code duplication without sacrificing +// the subclass split /** * Provides immunity to status effects to the user. - * @extends PreSetStatusEffectImmunityAbAttr */ export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} -/** - * Provides immunity to status effects to the user's field. - * @extends PreSetStatusEffectImmunityAbAttr - */ -export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} +export interface UserFieldStatusEffectImmunityAbAttrParams extends AbAttrBaseParams { + /** The status effect being applied */ + effect: StatusEffect; + /** Holds whether the status effect is prevented by the ability */ + cancelled: BooleanHolder; + /** The target of the status effect */ + target: Pokemon; + // TODO: It may be the case that callers are passing `null` in the case that the pokemon setting the status is the same as the target. + // Evaluate this and update the tsdoc accordingly. + /** The source of the status effect, or null if it is not coming from a pokemon */ + source: Pokemon | null; +} /** - * Conditionally provides immunity to status effects to the user's field. + * Provides immunity to status effects to the user's field. + */ +export class UserFieldStatusEffectImmunityAbAttr extends AbAttr { + protected immuneEffects: StatusEffect[]; + constructor(...immuneEffects: StatusEffect[]) { + super(); + + this.immuneEffects = immuneEffects; + } + + override canApply({ effect, cancelled }: UserFieldStatusEffectImmunityAbAttrParams): boolean { + return ( + (!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || + this.immuneEffects.includes(effect) + ); + } + + /** + * Set the `cancelled` value to true, indicating that the status effect is prevented. + */ + override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void { + cancelled.value = true; + } +} + +/** + * Conditionally provides immunity to status effects for the user's field. * * Used by {@linkcode AbilityId.FLOWER_VEIL | Flower Veil}. - * @extends UserFieldStatusEffectImmunityAbAttr - * */ export class ConditionalUserFieldStatusEffectImmunityAbAttr extends UserFieldStatusEffectImmunityAbAttr { /** * The condition for the field immunity to be applied. - * @param target The target of the status effect - * @param source The source of the status effect + * @param target - The target of the status effect + * @param source - The source of the status effect */ - protected condition: (target: Pokemon, source: Pokemon | null) => boolean; - - /** - * Evaluate the condition to determine if the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} can be applied. - * @param _pokemon The pokemon with the ability - * @param _passive unused - * @param _simulated Whether the ability is being simulated - * @param effect The status effect being applied - * @param cancelled Holds whether the status effect was cancelled by a prior effect - * @param args `Args[0]` is the target of the status effect, `Args[1]` is the source. - * @returns Whether the ability can be applied to cancel the status effect. - */ - override canApplyPreSetStatus( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - effect: StatusEffect, - cancelled: BooleanHolder, - args: [Pokemon, Pokemon | null, ...any], - ): boolean { - return ( - ((!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || - this.immuneEffects.includes(effect)) && - this.condition(args[0], args[1]) - ); - } + private condition: (target: Pokemon, source: Pokemon | null) => boolean; constructor(condition: (target: Pokemon, source: Pokemon | null) => boolean, ...immuneEffects: StatusEffect[]) { super(...immuneEffects); this.condition = condition; } + + /** + * Evaluate the condition to determine if the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} can be applied. + * @returns Whether the ability can be applied to cancel the status effect. + */ + override canApply(params: UserFieldStatusEffectImmunityAbAttrParams): boolean { + return this.condition(params.target, params.source) && super.canApply(params); + } + + /** + * Set the `cancelled` value to true, indicating that the status effect is prevented. + */ + override apply({ cancelled }: UserFieldStatusEffectImmunityAbAttrParams): void { + cancelled.value = true; + } +} + +export interface ConditionalUserFieldProtectStatAbAttrParams extends AbAttrBaseParams { + /** The stat being affected by the stat stage change */ + stat: BattleStat; + /** Holds whether the stat stage change is prevented by the ability */ + cancelled: BooleanHolder; + // TODO: consider making this required and not inherit from PreStatStageChangeAbAttr + /** The target of the stat stage change */ + target?: Pokemon; } /** @@ -4616,24 +3733,9 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA } /** - * Determine whether the {@linkcode ConditionalUserFieldProtectStatAbAttr} can be applied. - * @param _pokemon The pokemon with the ability - * @param _passive unused - * @param _simulated Unused - * @param stat The stat being affected - * @param cancelled Holds whether the stat change was already prevented. - * @param args Args[0] is the target pokemon of the stat change. - * @returns `true` if the ability can be applied + * @returns Whether the ability can be used to cancel the stat stage change. */ - override canApplyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: [Pokemon, ...any], - ): boolean { - const target = args[0]; + override canApply({ stat, cancelled, target }: ConditionalUserFieldProtectStatAbAttrParams): boolean { if (!target) { return false; } @@ -4646,53 +3748,37 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA /** * Apply the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} to an interaction - * @param _pokemon The pokemon the stat change is affecting (unused) - * @param _passive unused - * @param _simulated unused - * @param stat The stat being affected - * @param cancelled Will be set to true if the stat change is prevented - * @param _args unused */ - override applyPreStatStageChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _stat: BattleStat, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: ConditionalUserFieldProtectStatAbAttrParams): void { cancelled.value = true; } } -export class PreApplyBattlerTagAbAttr extends AbAttr { - canApplyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { - return true; - } - - applyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - _cancelled: BooleanHolder, - _args: any[], - ): void {} +export interface PreApplyBattlerTagAbAttrParams extends AbAttrBaseParams { + /** The tag being applied */ + tag: BattlerTag; + /** Holds whether the tag is prevented by the ability */ + cancelled: BooleanHolder; } /** - * Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets. + * Base class for ability attributes that apply their effect before a BattlerTag {@linkcode BattlerTag} is applied. + * + * ⚠️ Subclasses violate Liskov Substitution Principle, so this class must not be provided to {@linkcode applyAbAttrs} */ -export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { +export abstract class PreApplyBattlerTagAbAttr extends AbAttr { + canApply(_params: PreApplyBattlerTagAbAttrParams): boolean { + return true; + } + + apply(_params: PreApplyBattlerTagAbAttrParams): void {} +} + +// Intentionally not exported because this shouldn't be able to be passed to `applyAbAttrs`. It only exists so that +// PreApplyBattlerTagImmunityAbAttr and UserFieldPreApplyBattlerTagImmunityAbAttr can avoid code duplication +// while preserving type safety. (Since the UserField version require an additional parameter, target, in its apply methods) +abstract class BaseBattlerTagImmunityAbAttr

extends PreApplyBattlerTagAbAttr { protected immuneTagTypes: BattlerTagType[]; - protected battlerTag: BattlerTag; constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) { super(true); @@ -4700,75 +3786,57 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { this.immuneTagTypes = coerceArray(immuneTagTypes); } - override canApplyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - _args: any[], - ): boolean { - this.battlerTag = tag; - + override canApply({ cancelled, tag }: P): boolean { return !cancelled.value && this.immuneTagTypes.includes(tag.tagType); } - override applyPreApplyBattlerTag( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _tag: BattlerTag, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: P): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon, tag }: P, abilityName: string): string { return i18next.t("abilityTriggers:battlerTagImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - battlerTagName: this.battlerTag.getDescriptor(), + battlerTagName: tag.getDescriptor(), }); } } +// TODO: The battler tag ability attributes are in dire need of improvement +// It is unclear why there is a `PreApplyBattlerTagImmunityAbAttr` class that isn't used, +// and then why there's a BattlerTagImmunityAbAttr class as well. + +/** + * Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets. + * + * This does not check whether the tag is already applied; that check should happen in the caller. + */ +export class PreApplyBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr {} + /** * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user. - * @extends PreApplyBattlerTagImmunityAbAttr */ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} +export interface UserFieldBattlerTagImmunityAbAttrParams extends PreApplyBattlerTagAbAttrParams { + /** The pokemon that the battler tag is being applied to */ + target: Pokemon; +} /** * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user's field. - * @extends PreApplyBattlerTagImmunityAbAttr */ -export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} +export class UserFieldBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr {} export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattlerTagImmunityAbAttr { private condition: (target: Pokemon) => boolean; /** * Determine whether the {@linkcode ConditionalUserFieldBattlerTagImmunityAbAttr} can be applied by passing the target pokemon to the condition. - * @param pokemon The pokemon owning the ability - * @param passive unused - * @param simulated whether the ability is being simulated (unused) - * @param tag The {@linkcode BattlerTag} being applied - * @param cancelled Holds whether the tag was previously cancelled (unused) - * @param args Args[0] is the target that the tag is attempting to be applied to * @returns Whether the ability can be used to cancel the battler tag */ - override canApplyPreApplyBattlerTag( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - args: [Pokemon, ...any], - ): boolean { - return ( - super.canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args) && this.condition(args[0]) - ); + override canApply(params: UserFieldBattlerTagImmunityAbAttrParams): boolean { + return super.canApply(params) && this.condition(params.target); } constructor(condition: (target: Pokemon) => boolean, immuneTagTypes: BattlerTagType | BattlerTagType[]) { @@ -4778,6 +3846,13 @@ export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattl } } +export interface BlockCritAbAttrParams extends AbAttrBaseParams { + /** + * Holds a boolean that will be set to `true` if the user's ability prevents the attack from being a critical hit + */ + readonly blockCrit: BooleanHolder; +} + export class BlockCritAbAttr extends AbAttr { constructor() { super(false); @@ -4785,19 +3860,17 @@ export class BlockCritAbAttr extends AbAttr { /** * Apply the block crit ability by setting the value in the provided boolean holder to `true`. - * @param args - `[0]`: A {@linkcode BooleanHolder} containing whether the attack is prevented from critting. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: [BooleanHolder], - ): void { - args[0].value = true; + override apply({ blockCrit }: BlockCritAbAttrParams): void { + blockCrit.value = true; } } +export interface BonusCritAbAttrParams extends AbAttrBaseParams { + /** Holds the crit stage that may be modified by ability application */ + critStage: NumberHolder; +} + export class BonusCritAbAttr extends AbAttr { constructor() { super(false); @@ -4805,24 +3878,17 @@ export class BonusCritAbAttr extends AbAttr { /** * Apply the bonus crit ability by increasing the value in the provided number holder by 1 - * - * @param _pokemon The pokemon with the BonusCrit ability (unused) - * @param _passive Unused - * @param _simulated Unused - * @param _cancelled Unused - * @param args Args[0] is a number holder containing the crit stage. */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: [NumberHolder, ...any], - ): void { - (args[0] as NumberHolder).value += 1; + override apply({ critStage }: BonusCritAbAttrParams): void { + critStage.value += 1; } } +export interface MultCritAbAttrParams extends AbAttrBaseParams { + /** The critical hit multiplier that may be modified by ability application */ + critMult: NumberHolder; +} + export class MultCritAbAttr extends AbAttr { public multAmount: number; @@ -4832,27 +3898,26 @@ export class MultCritAbAttr extends AbAttr { this.multAmount = multAmount; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - const critMult = args[0] as NumberHolder; + override canApply({ critMult }: MultCritAbAttrParams): boolean { return critMult.value > 1; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const critMult = args[0] as NumberHolder; + override apply({ critMult }: MultCritAbAttrParams): void { critMult.value *= this.multAmount; } } +export interface ConditionalCritAbAttrParams extends AbAttrBaseParams { + /** Holds a boolean that will be set to true if the attack is guaranteed to crit */ + target: Pokemon; + /** The move being used */ + move: Move; + /** Holds whether the attack will critically hit */ + isCritical: BooleanHolder; +} + /** * Guarantees a critical hit according to the given condition, except if target prevents critical hits. ie. Merciless - * @extends AbAttr - * @see {@linkcode apply} */ export class ConditionalCritAbAttr extends AbAttr { private condition: PokemonAttackCondition; @@ -4863,26 +3928,12 @@ export class ConditionalCritAbAttr extends AbAttr { this.condition = condition; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - const target = args[1] as Pokemon; - const move = args[2] as Move; - return this.condition(pokemon, target, move); + override canApply({ isCritical, pokemon, target, move }: ConditionalCritAbAttrParams): boolean { + return !isCritical.value && this.condition(pokemon, target, move); } - /** - * @param _pokemon {@linkcode Pokemon} user. - * @param args [0] {@linkcode BooleanHolder} If true critical hit is guaranteed. - * [1] {@linkcode Pokemon} Target. - * [2] {@linkcode Move} used by ability user. - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as BooleanHolder).value = true; + override apply({ isCritical }: ConditionalCritAbAttrParams): void { + isCritical.value = true; } } @@ -4891,13 +3942,7 @@ export class BlockNonDirectDamageAbAttr extends AbAttr { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } @@ -4909,7 +3954,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { private effects: StatusEffect[]; /** - * @param {StatusEffect[]} effects The status effect(s) that will be blocked from damaging the ability pokemon + * @param effects - The status effect(s) that will be blocked from damaging the ability pokemon */ constructor(...effects: StatusEffect[]) { super(false); @@ -4917,51 +3962,42 @@ export class BlockStatusDamageAbAttr extends AbAttr { this.effects = effects; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrParamsWithCancel): boolean { return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect); } - /** - * @param {Pokemon} _pokemon The pokemon with the ability - * @param {boolean} _passive N/A - * @param {BooleanHolder} cancelled Whether to cancel the status damage - * @param {any[]} _args N/A - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } export class BlockOneHitKOAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ChangeMovePriorityAbAttrParams extends AbAttrBaseParams { + /** The move being used */ + move: Move; + /** The priority of the move being used */ + priority: NumberHolder; +} + /** * This governs abilities that alter the priority of moves * Abilities: Prankster, Gale Wings, Triage, Mycelium Might, Stall * Note - Quick Claw has a separate and distinct implementation outside of priority + * + * @sealed */ export class ChangeMovePriorityAbAttr extends AbAttr { private moveFunc: (pokemon: Pokemon, move: Move) => boolean; private changeAmount: number; /** - * @param {(pokemon, move) => boolean} moveFunc applies priority-change to moves within a provided category - * @param {number} changeAmount the amount of priority added or subtracted + * @param moveFunc - applies priority-change to moves that meet the condition + * @param changeAmount - The amount of priority added or subtracted */ constructor(moveFunc: (pokemon: Pokemon, move: Move) => boolean, changeAmount: number) { super(false); @@ -4970,46 +4006,39 @@ export class ChangeMovePriorityAbAttr extends AbAttr { this.changeAmount = changeAmount; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.moveFunc(pokemon, args[0] as Move); + override canApply({ pokemon, move }: ChangeMovePriorityAbAttrParams): boolean { + return this.moveFunc(pokemon, move); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[1] as NumberHolder).value += this.changeAmount; + override apply({ priority }: ChangeMovePriorityAbAttrParams): void { + priority.value += this.changeAmount; } } -export class IgnoreContactAbAttr extends AbAttr {} +export class IgnoreContactAbAttr extends AbAttr { + private declare readonly _: never; +} -export class PreWeatherEffectAbAttr extends AbAttr { - canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { +/** + * Shared interface for attributes that respond to a weather. + */ +export interface PreWeatherEffectAbAttrParams extends AbAttrParamsWithCancel { + /** The weather effect for the interaction. `null` is treated as no weather */ + weather: Weather | null; +} + +export abstract class PreWeatherEffectAbAttr extends AbAttr { + override canApply(_params: Closed): boolean { return true; } - applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _cancelled: BooleanHolder, - _args: any[], - ): void {} + override apply(_params: Closed): void {} } -export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr {} +/** + * Base class for abilities that apply an effect before a weather effect is applied. + */ +export abstract class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr {} export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { private weatherTypes: WeatherType[]; @@ -5020,57 +4049,36 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - override canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: Weather, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { - return !this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1; + override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean { + if (!weather) { + return false; + } + const weatherType = weather.weatherType; + return !this.weatherTypes.length || this.weatherTypes.includes(weatherType); } - override applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreWeatherEffectAbAttrParams): void { cancelled.value = true; } } export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { - public affectsImmutable: boolean; + public readonly affectsImmutable: boolean; - constructor(affectsImmutable?: boolean) { + constructor(affectsImmutable = false) { super(true); - this.affectsImmutable = !!affectsImmutable; + this.affectsImmutable = affectsImmutable; } - override canApplyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: Weather, - _cancelled: BooleanHolder, - _args: any[], - ): boolean { + override canApply({ weather }: PreWeatherEffectAbAttrParams): boolean { + if (!weather) { + return false; + } return this.affectsImmutable || weather.isImmutable(); } - override applyPreWeatherEffect( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: PreWeatherEffectAbAttrParams): void { cancelled.value = true; } } @@ -5188,12 +4196,18 @@ function getOncePerBattleCondition(ability: AbilityId): AbAttrCondition { }; } +/** + * @sealed + */ export class ForewarnAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { + if (!simulated) { + return; + } let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -5221,23 +4235,26 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } } - if (!simulated) { - globalScene.phaseManager.queueMessage( - i18next.t("abilityTriggers:forewarn", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - moveName: maxMove, - }), - ); - } + + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:forewarn", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + moveName: maxMove, + }), + ); } } +/** + * Ability attribute that reveals the abilities of all opposing Pokémon when the Pokémon with this ability is summoned. + * @sealed + */ export class FriskAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { for (const opponent of pokemon.getOpponents()) { globalScene.phaseManager.queueMessage( @@ -5253,30 +4270,27 @@ export class FriskAbAttr extends PostSummonAbAttr { } } -export class PostWeatherChangeAbAttr extends AbAttr { - canApplyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): boolean { +export interface PostWeatherChangeAbAttrParams extends AbAttrBaseParams { + /** The kind of the weather that was just changed to */ + weather: WeatherType; +} + +/** + * Base class for ability attributes that apply their effect after a weather change. + */ +export abstract class PostWeatherChangeAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** * Triggers weather-based form change when weather changes. * Used by Forecast and Flower Gift. - * @extends PostWeatherChangeAbAttr + * + * @sealed */ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { private ability: AbilityId; @@ -5289,13 +4303,7 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { this.formRevertingWeathers = formRevertingWeathers; } - override canApplyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: WeatherType, - _args: any[], - ): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { const isCastformWithForecast = pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST; const isCherrimWithFlowerGift = @@ -5307,23 +4315,15 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} - * @param _pokemon - The Pokemon with this ability - * @param _passive - unused - * @param simulated - unused - * @param _weather - unused - * @param _args - unused */ - override applyPostWeatherChange( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void { + override apply({ simulated }: AbAttrBaseParams): void { if (simulated) { return; } + // TODO: investigate why this is not using the weatherType parameter + // and is instead reading the weather from the global scene + const weatherType = globalScene.arena.weather?.weatherType; if (weatherType && this.formRevertingWeathers.includes(weatherType)) { @@ -5334,6 +4334,10 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { } } +/** + * Add a battler tag to the pokemon when the weather changes. + * @sealed + */ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr { private tagType: BattlerTagType; private turnCount: number; @@ -5347,29 +4351,18 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - override canApplyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - weather: WeatherType, - _args: any[], - ): boolean { - return !!this.weatherTypes.find(w => weather === w) && pokemon.canAddTag(this.tagType); + override canApply({ weather, pokemon }: PostWeatherChangeAbAttrParams): boolean { + return this.weatherTypes.includes(weather) && pokemon.canAddTag(this.tagType); } - override applyPostWeatherChange( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _weather: WeatherType, - _args: any[], - ): void { + override apply({ simulated, pokemon }: PostWeatherChangeAbAttrParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } } } +export type PostWeatherLapseAbAttrParams = Omit; export class PostWeatherLapseAbAttr extends AbAttr { protected weatherTypes: WeatherType[]; @@ -5379,23 +4372,11 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - canApplyPostWeatherLapse( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostWeatherLapse( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): void {} + apply(_params: Closed): void {} getCondition(): AbAttrCondition { return getWeatherCondition(...this.weatherTypes); @@ -5411,23 +4392,11 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - override canApplyPostWeatherLapse( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + override canApply({ pokemon }: PostWeatherLapseAbAttrParams): boolean { return !pokemon.isFullHp(); } - override applyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - _weather: Weather, - _args: any[], - ): void { + override apply({ pokemon, passive, simulated }: PostWeatherLapseAbAttrParams): void { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { globalScene.phaseManager.unshiftNew( @@ -5453,23 +4422,11 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - override canApplyPostWeatherLapse( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _weather: Weather | null, - _args: any[], - ): boolean { + override canApply({ pokemon }: PostWeatherLapseAbAttrParams): boolean { return !pokemon.hasAbilityWithAttr("BlockNonDirectDamageAbAttr"); } - override applyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - _weather: Weather, - _args: any[], - ): void { + override apply({ simulated, pokemon, passive }: PostWeatherLapseAbAttrParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.queueMessage( @@ -5485,24 +4442,17 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { } } +export interface PostTerrainChangeAbAttrParams extends AbAttrBaseParams { + /** The terrain type that is being changed to */ + terrain: TerrainType; +} + export class PostTerrainChangeAbAttr extends AbAttr { - canApplyPostTerrainChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostTerrainChange( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): void {} + apply(_params: Closed): void {} } export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr { @@ -5518,23 +4468,11 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - override canApplyPostTerrainChange( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - terrain: TerrainType, - _args: any[], - ): boolean { + override canApply({ pokemon, terrain }: PostTerrainChangeAbAttrParams): boolean { return !!this.terrainTypes.find(t => t === terrain) && pokemon.canAddTag(this.tagType); } - override applyPostTerrainChange( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _terrain: TerrainType, - _args: any[], - ): void { + override apply({ pokemon, simulated }: PostTerrainChangeAbAttrParams): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -5549,21 +4487,23 @@ function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { } export class PostTurnAbAttr extends AbAttr { - canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * This attribute will heal 1/8th HP if the ability pokemon has the correct status. + * + * @sealed */ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { private effects: StatusEffect[]; /** - * @param {StatusEffect[]} effects The status effect(s) that will qualify healing the ability pokemon + * @param effects - The status effect(s) that will qualify healing the ability pokemon */ constructor(...effects: StatusEffect[]) { super(false); @@ -5571,16 +4511,11 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { this.effects = effects; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !isNullOrUndefined(pokemon.status) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); } - /** - * @param {Pokemon} pokemon The pokemon with the ability that will receive the healing - * @param {Boolean} passive N/A - * @param {any[]} _args N/A - */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, passive, pokemon }: AbAttrBaseParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -5597,6 +4532,8 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { /** * After the turn ends, resets the status of either the ability holder or their ally * @param allyTarget Whether to target ally, defaults to false (self-target) + * + * @sealed */ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { private allyTarget: boolean; @@ -5607,7 +4544,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { @@ -5618,7 +4555,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { return !!effect && effect !== StatusEffect.FAINT; } - override applyPostTurn(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated }: AbAttrBaseParams): void { if (!simulated && this.target?.status) { globalScene.phaseManager.queueMessage( getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target)), @@ -5648,7 +4585,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { super(); } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { // Ensure we have at least 1 recoverable berry (at least 1 berry in berriesEaten is not capped) const cappedBerries = new Set( globalScene @@ -5668,7 +4605,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { return this.procChance(pokemon) >= pass; } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { this.createEatenBerry(pokemon); } @@ -5716,35 +4653,20 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { /** * Attribute to track and re-trigger last turn's berries at the end of the `BerryPhase`. + * Must only be used by Cud Chew! Do _not_ reuse this attribute for anything else * Used by {@linkcode AbilityId.CUD_CHEW}. + * @sealed */ -export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { +export class CudChewConsumeBerryAbAttr extends AbAttr { /** * @returns `true` if the pokemon ate anything last turn */ - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - // force ability popup for ability triggers on normal turns. - // Still not used if ability doesn't proc - this.showAbility = true; + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !!pokemon.summonData.berriesEatenLast.length; } - /** - * Cause this {@linkcode Pokemon} to regurgitate and eat all berries inside its `berriesEatenLast` array. - * Triggers a berry use animation, but does *not* count for other berry or item-related abilities. - * @param pokemon - The {@linkcode Pokemon} having a bad tummy ache - * @param _passive - N/A - * @param _simulated - N/A - * @param _cancelled - N/A - * @param _args - N/A - */ - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + override apply({ pokemon }: AbAttrBaseParams): void { + // TODO: Consider respecting the `simulated` flag globalScene.phaseManager.unshiftNew( "CommonAnimPhase", pokemon.getBattlerIndex(), @@ -5761,27 +4683,27 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { } // uncomment to make cheek pouch work with cud chew - // applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false)); + // applyAbAttrs("HealFromBerryUseAbAttr", {pokemon}); } +} - /** - * @returns always `true` as we always want to move berries into summon data - */ - override canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { - this.showAbility = false; // don't show popup for turn end berry moving (should ideally be hidden) - return true; +/** + * Consume a berry at the end of the turn if the pokemon has one. + * + * Must be used in conjunction with {@linkcode CudChewConsumeBerryAbAttr}, and is + * only used by {@linkcode AbilityId.CUD_CHEW}. + */ +export class CudChewRecordBerryAbAttr extends PostTurnAbAttr { + constructor() { + super(false); } /** * Move this {@linkcode Pokemon}'s `berriesEaten` array from `PokemonTurnData` * into `PokemonSummonData` on turn end. * Both arrays are cleared on switch. - * @param pokemon - The {@linkcode Pokemon} having a nice snack - * @param _passive - N/A - * @param _simulated - N/A - * @param _args - N/A */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.summonData.berriesEatenLast = pokemon.turnData.berriesEaten; } } @@ -5795,16 +4717,14 @@ export class MoodyAbAttr extends PostTurnAbAttr { } /** * Randomly increases one stat stage by 2 and decreases a different stat stage by 1 - * @param {Pokemon} pokemon Pokemon that has this ability - * @param _passive N/A - * @param simulated true if applying in a simulated call. - * @param _args N/A - * * Any stat stages at +6 or -6 are excluded from being increased or decreased, respectively * If the pokemon already has all stat stages raised to 6, it will only decrease one stat stage by 1 * If the pokemon already has all stat stages lowered to -6, it will only increase one stat stage by 2 */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } const canRaise = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) < 6); let canLower = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) > -6); @@ -5822,26 +4742,28 @@ export class MoodyAbAttr extends PostTurnAbAttr { } } +/** @sealed */ export class SpeedBoostAbAttr extends PostTurnAbAttr { constructor() { super(true); } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean { + // todo: Consider moving the `simulated` check to the `apply` method return simulated || (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway); } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.SPD], 1); } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isFullHp(); } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon, passive }: AbAttrBaseParams): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.phaseManager.unshiftNew( @@ -5858,6 +4780,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { } } +/** @sealed */ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { private formFunc: (p: Pokemon) => number; @@ -5867,11 +4790,11 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { this.formFunc = formFunc; } - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -5880,9 +4803,10 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { /** * Attribute used for abilities (Bad Dreams) that damages the opponents for being asleep + * @sealed */ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return pokemon .getOpponents() .some( @@ -5892,26 +4816,21 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { !opp.switchOutStatus, ); } - /** - * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) - * @param pokemon {@linkcode Pokemon} with this ability - * @param _passive N/A - * @param simulated `true` if applying in a simulated call. - * @param _args N/A - */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + /** Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) */ + override apply({ pokemon, simulated }: AbAttrBaseParams): void { + if (simulated) { + return; + } for (const opp of pokemon.getOpponents()) { if ( (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && !opp.hasAbilityWithAttr("BlockNonDirectDamageAbAttr") && !opp.switchOutStatus ) { - if (!simulated) { - opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); - globalScene.phaseManager.queueMessage( - i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }), - ); - } + opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }), + ); } } } @@ -5919,20 +4838,17 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { /** * Grabs the last failed Pokeball used - * @extends PostTurnAbAttr + * @sealed * @see {@linkcode applyPostTurn} */ export class FetchBallAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { + override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean { return !simulated && !isNullOrUndefined(globalScene.currentBattle.lastUsedPokeball) && !!pokemon.isPlayer; } /** * Adds the last used Pokeball back into the player's inventory - * @param pokemon {@linkcode Pokemon} with this ability - * @param _passive N/A - * @param _args N/A */ - override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { const lastUsed = globalScene.currentBattle.lastUsedPokeball; globalScene.pokeballCounts[lastUsed!]++; globalScene.currentBattle.lastUsedPokeball = null; @@ -5945,7 +4861,9 @@ export class FetchBallAbAttr extends PostTurnAbAttr { } } -export class PostBiomeChangeAbAttr extends AbAttr {} +export class PostBiomeChangeAbAttr extends AbAttr { + private declare readonly _: never; +} export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { private weatherType: WeatherType; @@ -5956,23 +4874,18 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return (globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType); } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } } } +/** @sealed */ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { private terrainType: TerrainType; @@ -5982,47 +4895,35 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply(_params: AbAttrBaseParams): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } } } +export interface PostMoveUsedAbAttrParams extends AbAttrBaseParams { + /** The move that was used */ + move: PokemonMove; + /** The source of the move */ + source: Pokemon; + /** The targets of the move */ + targets: BattlerIndex[]; +} + /** * Triggers just after a move is used either by the opponent or the player - * @extends AbAttr */ export class PostMoveUsedAbAttr extends AbAttr { - canApplyPostMoveUsed( - _pokemon: Pokemon, - _move: PokemonMove, - _source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostMoveUsed( - _pokemon: Pokemon, - _move: PokemonMove, - _source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): void {} + apply(_params: Closed): void {} } /** @@ -6030,14 +4931,7 @@ export class PostMoveUsedAbAttr extends AbAttr { * @extends PostMoveUsedAbAttr */ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { - override canApplyPostMoveUsed( - dancer: Pokemon, - _move: PokemonMove, - source: Pokemon, - _targets: BattlerIndex[], - _simulated: boolean, - _args: any[], - ): boolean { + override canApply({ source, pokemon }: PostMoveUsedAbAttrParams): boolean { // List of tags that prevent the Dancer from replicating the move const forbiddenTags = [ BattlerTagType.FLYING, @@ -6047,40 +4941,28 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { ]; // The move to replicate cannot come from the Dancer return ( - source.getBattlerIndex() !== dancer.getBattlerIndex() && - !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) + source.getBattlerIndex() !== pokemon.getBattlerIndex() && + !pokemon.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) ); } /** * Resolves the Dancer ability by replicating the move used by the source of the dance * either on the source itself or on the target of the dance - * @param dancer {@linkcode Pokemon} with Dancer ability - * @param move {@linkcode PokemonMove} Dancing move used by the source - * @param source {@linkcode Pokemon} that used the dancing move - * @param targets {@linkcode BattlerIndex}Targets of the dancing move - * @param _args N/A */ - override applyPostMoveUsed( - dancer: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated: boolean, - _args: any[], - ): void { + override apply({ source, pokemon, move, targets, simulated }: PostMoveUsedAbAttrParams): void { if (!simulated) { - dancer.turnData.extraTurns++; + pokemon.turnData.extraTurns++; // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove().is("AttackMove") || move.getMove().is("StatusMove")) { - const target = this.getTarget(dancer, source, targets); - globalScene.phaseManager.unshiftNew("MovePhase", dancer, target, move, MoveUseMode.INDIRECT); + const target = this.getTarget(pokemon, source, targets); + globalScene.phaseManager.unshiftNew("MovePhase", pokemon, target, move, MoveUseMode.INDIRECT); } else if (move.getMove().is("SelfStatusMove")) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself globalScene.phaseManager.unshiftNew( "MovePhase", - dancer, - [dancer.getBattlerIndex()], + pokemon, + [pokemon.getBattlerIndex()], move, MoveUseMode.INDIRECT, ); @@ -6091,9 +4973,9 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { /** * Get the correct targets of Dancer ability * - * @param dancer {@linkcode Pokemon} Pokemon with Dancer ability - * @param source {@linkcode Pokemon} Source of the dancing move - * @param targets {@linkcode BattlerIndex} Targets of the dancing move + * @param dancer - Pokemon with Dancer ability + * @param source - Source of the dancing move + * @param targets - Targets of the dancing move */ getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]): BattlerIndex[] { if (dancer.isPlayer()) { @@ -6108,16 +4990,15 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @extends AbAttr */ export class PostItemLostAbAttr extends AbAttr { - canApplyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } /** * Applies a Battler Tag to the Pokemon after it loses or consumes an item - * @extends PostItemLostAbAttr */ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { private tagType: BattlerTagType; @@ -6126,7 +5007,7 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { this.tagType = tagType; } - override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean { return !pokemon.getTag(this.tagType) && !simulated; } @@ -6135,11 +5016,15 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { * @param pokemon {@linkcode Pokemon} with this ability * @param _args N/A */ - override applyPostItemLost(pokemon: Pokemon, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.addTag(this.tagType); } } +export interface StatStageChangeMultiplierAbAttrParams extends AbAttrBaseParams { + /** Holder for the stages after applying the ability. */ + numStages: NumberHolder; +} export class StatStageChangeMultiplierAbAttr extends AbAttr { private multiplier: number; @@ -6149,32 +5034,27 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.multiplier; + override apply({ numStages }: StatStageChangeMultiplierAbAttrParams): void { + numStages.value *= this.multiplier; } } +export interface StatStageChangeCopyAbAttrParams extends AbAttrBaseParams { + /** The stats to change */ + stats: BattleStat[]; + /** The number of stages that were changed by the original */ + numStages: number; +} + export class StatStageChangeCopyAbAttr extends AbAttr { - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ pokemon, stats, numStages, simulated }: StatStageChangeCopyAbAttrParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", pokemon.getBattlerIndex(), true, - args[0] as BattleStat[], - args[1] as number, + stats, + numStages, true, false, false, @@ -6184,21 +5064,21 @@ export class StatStageChangeCopyAbAttr extends AbAttr { } export class BypassBurnDamageReductionAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ReduceBurnDamageAbAttrParams extends AbAttrBaseParams { + /** Holds the damage done by the burn */ + burnDamage: NumberHolder; +} + /** * Causes Pokemon to take reduced damage from the {@linkcode StatusEffect.BURN | Burn} status * @param multiplier Multiplied with the damage taken @@ -6210,31 +5090,20 @@ export class ReduceBurnDamageAbAttr extends AbAttr { /** * Applies the damage reduction - * @param _pokemon N/A - * @param _passive N/A - * @param _cancelled N/A - * @param args `[0]` {@linkcode NumberHolder} The damage value being modified */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.multiplier); + override apply({ burnDamage }: ReduceBurnDamageAbAttrParams): void { + burnDamage.value = toDmgValue(burnDamage.value * this.multiplier); } } +export interface DoubleBerryEffectAbAttrParams extends AbAttrBaseParams { + /** The value of the berry effect that will be doubled by the ability's application */ + effectValue: NumberHolder; +} + export class DoubleBerryEffectAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= 2; + override apply({ effectValue }: DoubleBerryEffectAbAttrParams): void { + effectValue.value *= 2; } } @@ -6245,12 +5114,8 @@ export class DoubleBerryEffectAbAttr extends AbAttr { export class PreventBerryUseAbAttr extends AbAttr { /** * Prevent use of opposing berries. - * @param _pokemon - Unused - * @param _passive - Unused - * @param _simulated - Unused - * @param cancelled - {@linkcode BooleanHolder} containing whether to block berry use */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, cancelled: BooleanHolder): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } @@ -6258,7 +5123,6 @@ export class PreventBerryUseAbAttr extends AbAttr { /** * A Pokemon with this ability heals by a percentage of their maximum hp after eating a berry * @param healPercent - Percent of Max HP to heal - * @see {@linkcode apply()} for implementation */ export class HealFromBerryUseAbAttr extends AbAttr { /** Percent of Max HP to heal */ @@ -6271,7 +5135,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ..._args: [BooleanHolder, any[]]): void { + override apply({ simulated, passive, pokemon }: AbAttrBaseParams): void { if (simulated) { return; } @@ -6290,15 +5154,14 @@ export class HealFromBerryUseAbAttr extends AbAttr { } } +export interface RunSuccessAbAttrParams extends AbAttrBaseParams { + /** Holder for the likelihood that the pokemon will flee */ + chance: NumberHolder; +} + export class RunSuccessAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 256; + override apply({ chance }: RunSuccessAbAttrParams): void { + chance.value = 256; } } @@ -6318,50 +5181,33 @@ export class CheckTrappedAbAttr extends AbAttr { this.arenaTrapCondition = condition; } - canApplyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): boolean { + override canApply(_params: Closed): boolean { return true; } - applyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): void {} + override apply(_params: Closed): void {} +} + +export interface CheckTrappedAbAttrParams extends AbAttrBaseParams { + /** The pokemon to attempt to trap */ + opponent: Pokemon; + /** Holds whether the other Pokemon will be trapped or not */ + trapped: BooleanHolder; } /** * Determines whether a Pokemon is blocked from switching/running away * because of a trapping ability or move. - * @extends CheckTrappedAbAttr - * @see {@linkcode applyCheckTrapped} */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { - override canApplyCheckTrapped( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _trapped: BooleanHolder, - otherPokemon: Pokemon, - _args: any[], - ): boolean { + override canApply({ pokemon, opponent }: CheckTrappedAbAttrParams): boolean { return ( - this.arenaTrapCondition(pokemon, otherPokemon) && + this.arenaTrapCondition(pokemon, opponent) && !( - otherPokemon.getTypes(true).includes(PokemonType.GHOST) || - (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && - otherPokemon.getTypes().includes(PokemonType.GHOST)) + opponent.getTypes(true).includes(PokemonType.GHOST) || + (opponent.getTypes(true).includes(PokemonType.STELLAR) && opponent.getTypes().includes(PokemonType.GHOST)) ) && - !otherPokemon.hasAbility(AbilityId.RUN_AWAY) + !opponent.hasAbility(AbilityId.RUN_AWAY) ); } @@ -6371,24 +5217,12 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * If the enemy has the ability Run Away, it is not trapped. * If the user has Magnet Pull and the enemy is not a Steel type, it is not trapped. * If the user has Arena Trap and the enemy is not grounded, it is not trapped. - * @param _pokemon The {@link Pokemon} with this {@link AbAttr} - * @param _passive N/A - * @param trapped {@link BooleanHolder} indicating whether the other Pokemon is trapped or not - * @param _otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability - * @param _args N/A */ - override applyCheckTrapped( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - trapped: BooleanHolder, - _otherPokemon: Pokemon, - _args: any[], - ): void { + override apply({ trapped }: CheckTrappedAbAttrParams): void { trapped.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: CheckTrappedAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:arenaTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6396,50 +5230,52 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } } +export interface MaxMultiHitAbAttrParams extends AbAttrBaseParams { + /** The number of hits that the move will do */ + hits: NumberHolder; +} + export class MaxMultiHitAbAttr extends AbAttr { constructor() { super(false); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value = 0; + override apply({ hits }: MaxMultiHitAbAttrParams): void { + hits.value = 0; } } -export class PostBattleAbAttr extends AbAttr { +export interface PostBattleAbAttrParams extends AbAttrBaseParams { + /** Whether the battle that just ended was a victory */ + victory: boolean; +} + +export abstract class PostBattleAbAttr extends AbAttr { + private declare readonly _: never; constructor(showAbility = true) { super(showAbility); } - canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } - applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} + apply(_params: Closed): void {} } export class PostBattleLootAbAttr extends PostBattleAbAttr { private randItem?: PokemonHeldItemModifier; - override canApplyPostBattle(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply({ simulated, victory, pokemon }: PostBattleAbAttrParams): boolean { const postBattleLoot = globalScene.currentBattle.postBattleLoot; - if (!simulated && postBattleLoot.length && args[0]) { + if (!simulated && postBattleLoot.length && victory) { this.randItem = randSeedItem(postBattleLoot); return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1); } return false; } - /** - * @param _args - `[0]`: boolean for if the battle ended in a victory - */ - override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: PostBattleAbAttrParams): void { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!this.randItem) { this.randItem = randSeedItem(postBattleLoot); @@ -6458,67 +5294,41 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } } -export class PostFaintAbAttr extends AbAttr { - canApplyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { +/** + * Shared parameters for ability attributes that are triggered after the user faints. + */ +export interface PostFaintAbAttrParams extends AbAttrBaseParams { + /** The pokemon that caused the faint, or undefined if not caused by a pokemon */ + readonly attacker?: Pokemon; + /** The move that caused the faint, or undefined if not caused by a move */ + readonly move?: Move; + /** The result of the hit that caused the faint */ + readonly hitResult?: HitResult; +} + +export abstract class PostFaintAbAttr extends AbAttr { + canApply(_params: Closed): boolean { return true; } - applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void {} + apply(_params: Closed): void {} } /** * Used for weather suppressing abilities to trigger weather-based form changes upon being fainted. * Used by Cloud Nine and Air Lock. - * @extends PostFaintAbAttr + * @sealed */ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAttr { - override canApplyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { + override canApply(_params: PostFaintAbAttrParams): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} * when the user of the ability faints - * @param {Pokemon} _pokemon the fainted Pokemon - * @param _passive n/a - * @param _attacker n/a - * @param _move n/a - * @param _hitResult n/a - * @param _args n/a */ - override applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ simulated }: PostFaintAbAttrParams): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChanges(); } @@ -6534,42 +5344,35 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - override canApplyPostFaint( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): boolean { + override canApply({ pokemon, attacker, move, simulated }: PostFaintAbAttrParams): boolean { + if (!move || !attacker) { + return false; + } const diedToDirectDamage = - move !== undefined && attacker !== undefined && move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }); const cancelled = new BooleanHolder(false); - globalScene.getField(true).map(p => applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled, simulated)); - return !(!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")); - } - - override applyPostFaint( - _pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - _move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void { - if (!simulated) { - attacker!.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { - result: HitResult.INDIRECT, + for (const otherPokemon of globalScene.getField(true)) { + applyAbAttrs("FieldPreventExplosiveMovesAbAttr", { + pokemon: otherPokemon, + simulated, + cancelled, }); - attacker!.turnData.damageTaken += toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); } + return !(!diedToDirectDamage || cancelled.value || attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr")); } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override apply({ simulated, attacker }: PostFaintAbAttrParams): void { + if (!attacker || simulated) { + return; + } + attacker.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { + result: HitResult.INDIRECT, + }); + attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); + } + + getTriggerMessage({ pokemon }: PostFaintAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postFaintContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6579,17 +5382,10 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { /** * Attribute used for abilities (Innards Out) that damage the opponent based on how much HP the last attack used to knock out the owner of the ability. + * @sealed */ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { - override applyPostFaint( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - attacker?: Pokemon, - move?: Move, - _hitResult?: HitResult, - ..._args: any[] - ): void { + override apply({ simulated, pokemon, move, attacker }: PostFaintAbAttrParams): void { //If the mon didn't die to indirect damage if (move !== undefined && attacker !== undefined && !simulated) { const damage = pokemon.turnData.attacksReceived[0].damage; @@ -6598,7 +5394,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + getTriggerMessage({ pokemon }: PostFaintAbAttrParams, abilityName: string): string { return i18next.t("abilityTriggers:postFaintHpDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -6606,45 +5402,41 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } } -/** - * Redirects a move to the pokemon with this ability if it meets the conditions - */ -export class RedirectMoveAbAttr extends AbAttr { - /** - * @param pokemon - The Pokemon with the redirection ability - * @param args - The args passed to the `AbAttr`: - * - `[0]` - The id of the {@linkcode Move} used - * - `[1]` - The target's battler index (before redirection) - * - `[2]` - The Pokemon that used the move being redirected - */ +export interface RedirectMoveAbAttrParams extends AbAttrBaseParams { + /** The id of the move being redirected */ + moveId: MoveId; + /** The target's battler index before redirection */ + targetIndex: NumberHolder; + /** The Pokemon that used the move being redirected */ + sourcePokemon: Pokemon; +} - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - if (!this.canRedirect(args[0] as MoveId, args[2] as Pokemon)) { +/** + * Base class for abilities that redirect moves to the pokemon with this ability. + */ +export abstract class RedirectMoveAbAttr extends AbAttr { + override canApply({ pokemon, moveId, targetIndex, sourcePokemon }: RedirectMoveAbAttrParams): boolean { + if (!this.canRedirect(moveId, sourcePokemon)) { return false; } - const target = args[1] as NumberHolder; const newTarget = pokemon.getBattlerIndex(); - return target.value !== newTarget; + return targetIndex.value !== newTarget; } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const target = args[1] as NumberHolder; + override apply({ pokemon, targetIndex }: RedirectMoveAbAttrParams): void { const newTarget = pokemon.getBattlerIndex(); - target.value = newTarget; + targetIndex.value = newTarget; } - canRedirect(moveId: MoveId, _user: Pokemon): boolean { + protected canRedirect(moveId: MoveId, _user: Pokemon): boolean { const move = allMoves[moveId]; return !![MoveTarget.NEAR_OTHER, MoveTarget.OTHER].find(t => move.moveTarget === t); } } +/** + * @sealed + */ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { public type: PokemonType; @@ -6653,17 +5445,27 @@ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { this.type = type; } - canRedirect(moveId: MoveId, user: Pokemon): boolean { + protected override canRedirect(moveId: MoveId, user: Pokemon): boolean { return super.canRedirect(moveId, user) && user.getMoveType(allMoves[moveId]) === this.type; } } -export class BlockRedirectAbAttr extends AbAttr {} +export class BlockRedirectAbAttr extends AbAttr { + private declare readonly _: never; +} + +export interface ReduceStatusEffectDurationAbAttrParams extends AbAttrBaseParams { + /** The status effect in question */ + statusEffect: StatusEffect; + /** Holds the number of turns until the status is healed, which may be modified by ability application. */ + duration: NumberHolder; +} /** * Used by Early Bird, makes the pokemon wake up faster * @param statusEffect - The {@linkcode StatusEffect} to check for * @see {@linkcode apply} + * @sealed */ export class ReduceStatusEffectDurationAbAttr extends AbAttr { private statusEffect: StatusEffect; @@ -6674,8 +5476,8 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { this.statusEffect = statusEffect; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return args[1] instanceof NumberHolder && args[0] === this.statusEffect; + override canApply({ statusEffect }: ReduceStatusEffectDurationAbAttrParams): boolean { + return statusEffect === this.statusEffect; } /** @@ -6684,21 +5486,24 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { * - `[0]` - The {@linkcode StatusEffect} of the Pokemon * - `[1]` - The number of turns remaining until the status is healed */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - args[1].value -= 1; + override apply({ duration }: ReduceStatusEffectDurationAbAttrParams): void { + duration.value -= 1; } } -export class FlinchEffectAbAttr extends AbAttr { +/** + * Base class for abilities that apply an effect when the user is flinched. + */ +export abstract class FlinchEffectAbAttr extends AbAttr { constructor() { super(true); } + + canApply(_params: Closed): boolean { + return true; + } + + apply(_params: Closed): void {} } export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { @@ -6712,13 +5517,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { this.stages = stages; } - override apply( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ simulated, pokemon }: AbAttrBaseParams): void { if (!simulated) { globalScene.phaseManager.unshiftNew( "StatStageChangePhase", @@ -6731,44 +5530,47 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { } } -export class IncreasePpAbAttr extends AbAttr {} +export class IncreasePpAbAttr extends AbAttr { + private declare readonly _: never; +} +/** @sealed */ export class ForceSwitchOutImmunityAbAttr extends AbAttr { - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: AbAttrParamsWithCancel): void { cancelled.value = true; } } +export interface ReduceBerryUseThresholdAbAttrParams extends AbAttrBaseParams { + /** Holds the hp ratio for the berry to proc, which may be modified by ability application */ + hpRatioReq: NumberHolder; +} + +/** @sealed */ export class ReduceBerryUseThresholdAbAttr extends AbAttr { constructor() { super(false); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + override canApply({ pokemon, hpRatioReq }: ReduceBerryUseThresholdAbAttrParams): boolean { const hpRatio = pokemon.getHpRatio(); - return args[0].value < hpRatio; + return hpRatioReq.value < hpRatio; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - args[0].value *= 2; + override apply({ hpRatioReq }: ReduceBerryUseThresholdAbAttrParams): void { + hpRatioReq.value *= 2; } } +export interface WeightMultiplierAbAttrParams extends AbAttrBaseParams { + /** The weight of the Pokemon, which may be modified by ability application */ + weight: NumberHolder; +} + /** * Ability attribute used for abilites that change the ability owner's weight * Used for Heavy Metal (doubling weight) and Light Metal (halving weight) + * @sealed */ export class WeightMultiplierAbAttr extends AbAttr { private multiplier: number; @@ -6779,33 +5581,34 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as NumberHolder).value *= this.multiplier; + override apply({ weight }: WeightMultiplierAbAttrParams): void { + weight.value *= this.multiplier; } } +export interface SyncEncounterNatureAbAttrParams extends AbAttrBaseParams { + /** The Pokemon whose nature is being synced */ + target: Pokemon; +} + +/** @sealed */ export class SyncEncounterNatureAbAttr extends AbAttr { constructor() { super(false); } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - (args[0] as Pokemon).setNature(pokemon.getNature()); + override apply({ target, pokemon }: SyncEncounterNatureAbAttrParams): void { + target.setNature(pokemon.getNature()); } } +export interface MoveAbilityBypassAbAttrParams extends AbAttrBaseParams { + /** The move being used */ + move: Move; + /** Holds whether the move's ability should be ignored */ + cancelled: BooleanHolder; +} + export class MoveAbilityBypassAbAttr extends AbAttr { private moveIgnoreFunc: (pokemon: Pokemon, move: Move) => boolean; @@ -6815,49 +5618,49 @@ export class MoveAbilityBypassAbAttr extends AbAttr { this.moveIgnoreFunc = moveIgnoreFunc || ((_pokemon, _move) => true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.moveIgnoreFunc(pokemon, args[0] as Move); + override canApply({ pokemon, move }: MoveAbilityBypassAbAttrParams): boolean { + return this.moveIgnoreFunc(pokemon, move); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: MoveAbilityBypassAbAttrParams): void { cancelled.value = true; } } -export class AlwaysHitAbAttr extends AbAttr {} +export class AlwaysHitAbAttr extends AbAttr { + private declare readonly _: never; +} /** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */ -export class IgnoreProtectOnContactAbAttr extends AbAttr {} +export class IgnoreProtectOnContactAbAttr extends AbAttr { + private declare readonly _: never; +} + +export interface InfiltratorAbAttrParams extends AbAttrBaseParams { + /** Holds a flag indicating that infiltrator's bypass is active */ + bypassed: BooleanHolder; +} /** * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Infiltrator_(Ability) | Infiltrator}. * Allows the source's moves to bypass the effects of opposing Light Screen, Reflect, Aurora Veil, Safeguard, Mist, and Substitute. + * @sealed */ export class InfiltratorAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return args[0] instanceof BooleanHolder; + /** @returns Whether bypassed has not yet been set */ + override canApply({ bypassed }: InfiltratorAbAttrParams): boolean { + return !bypassed.value; } /** * Sets a flag to bypass screens, Substitute, Safeguard, and Mist - * @param _pokemon n/a - * @param _passive n/a - * @param _simulated n/a - * @param _cancelled n/a - * @param args `[0]` a {@linkcode BooleanHolder | BooleanHolder} containing the flag */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: null, args: any[]): void { - const bypassed = args[0]; + override apply({ bypassed }: InfiltratorAbAttrParams): void { bypassed.value = true; } } @@ -6866,21 +5669,38 @@ export class InfiltratorAbAttr extends AbAttr { * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Magic_Bounce_(ability) | Magic Bounce}. * Allows the source to bounce back {@linkcode MoveFlags.REFLECTABLE | Reflectable} * moves as if the user had used {@linkcode MoveId.MAGIC_COAT | Magic Coat}. + * @sealed */ -export class ReflectStatusMoveAbAttr extends AbAttr {} +export class ReflectStatusMoveAbAttr extends AbAttr { + private declare readonly _: never; +} +/** @sealed */ export class NoTransformAbilityAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } } +/** @sealed */ export class NoFusionAbilityAbAttr extends AbAttr { + private declare readonly _: never; constructor() { super(false); } } +export interface IgnoreTypeImmunityAbAttrParams extends AbAttrBaseParams { + /** The type of the move being used */ + readonly moveType: PokemonType; + /** The type being checked for */ + readonly defenderType: PokemonType; + /** Holds whether the type immunity should be bypassed */ + cancelled: BooleanHolder; +} + +/** @sealed */ export class IgnoreTypeImmunityAbAttr extends AbAttr { private defenderType: PokemonType; private allowedMoveTypes: PokemonType[]; @@ -6891,23 +5711,25 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { this.allowedMoveTypes = allowedMoveTypes; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType); + override canApply({ moveType, defenderType }: IgnoreTypeImmunityAbAttrParams): boolean { + return this.defenderType === defenderType && this.allowedMoveTypes.includes(moveType); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: IgnoreTypeImmunityAbAttrParams): void { cancelled.value = true; } } +export interface IgnoreTypeStatusEffectImmunityAbAttrParams extends AbAttrParamsWithCancel { + /** The status effect being applied */ + readonly statusEffect: StatusEffect; + /** Holds whether the type immunity should be bypassed */ + readonly defenderType: PokemonType; +} + /** * Ignores the type immunity to Status Effects of the defender if the defender is of a certain type + * @sealed */ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { private statusEffect: StatusEffect[]; @@ -6920,17 +5742,11 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { this.defenderType = defenderType; } - override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { - return this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType); + override canApply({ statusEffect, defenderType, cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): boolean { + return !cancelled.value && this.statusEffect.includes(statusEffect) && this.defenderType.includes(defenderType); } - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ cancelled }: IgnoreTypeStatusEffectImmunityAbAttrParams): void { cancelled.value = true; } } @@ -6939,65 +5755,43 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { * Gives money to the user after the battle. * * @extends PostBattleAbAttr - * @see {@linkcode applyPostBattle} */ export class MoneyAbAttr extends PostBattleAbAttr { - override canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { - return !simulated && args[0]; + override canApply({ simulated, victory }: PostBattleAbAttrParams): boolean { + // TODO: Consider moving the simulated check to the apply method + return !simulated && victory; } - /** - * @param _pokemon {@linkcode Pokemon} that is the user of this ability. - * @param _passive N/A - * @param _args - `[0]`: boolean for if the battle ended in a victory - */ - override applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply(_params: PostBattleAbAttrParams): void { globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); } } +// TODO: Consider removing this class and just using the PostSummonStatStageChangeAbAttr with a conditionalAttr +// that checks for the presence of the tag. /** * Applies a stat change after a Pokémon is summoned, * conditioned on the presence of a specific arena tag. - * - * @extends PostSummonStatStageChangeAbAttr + * @sealed */ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageChangeAbAttr { - /** - * The type of arena tag that conditions the stat change. - * @private - */ - private tagType: ArenaTagType; + /** The type of arena tag that conditions the stat change. */ + private arenaTagType: ArenaTagType; /** * Creates an instance of PostSummonStatStageChangeOnArenaAbAttr. * Initializes the stat change to increase Attack by 1 stage if the specified arena tag is present. * - * @param {ArenaTagType} tagType - The type of arena tag to check for. + * @param tagType - The type of arena tag to check for. */ constructor(tagType: ArenaTagType) { super([Stat.ATK], 1, true, false); - this.tagType = tagType; + this.arenaTagType = tagType; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - return ( - (globalScene.arena.getTagOnSide(this.tagType, side) ?? false) && - super.canApplyPostSummon(pokemon, passive, simulated, args) - ); - } - - /** - * Applies the post-summon stat change if the specified arena tag is present on pokemon's side. - * This is used in Wind Rider ability. - * - * @param {Pokemon} pokemon - The Pokémon being summoned. - * @param {boolean} passive - Whether the effect is passive. - * @param {any[]} args - Additional arguments. - */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - super.applyPostSummon(pokemon, passive, simulated, args); + override canApply(params: AbAttrBaseParams): boolean { + const side = params.pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + return (globalScene.arena.getTagOnSide(this.arenaTagType, side) ?? false) && super.canApply(params); } } @@ -7006,7 +5800,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC * This is used in the Disguise and Ice Face abilities. * * Does not apply to a user's substitute - * @extends ReceivedMoveDamageMultiplierAbAttr + * @sealed */ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; @@ -7029,40 +5823,18 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { this.triggerMessageFunc = triggerMessageFunc; } - override canApplyPreDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - attacker: Pokemon, - move: Move, - _cancelled: BooleanHolder | null, - _args: any[], - ): boolean { - return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + override canApply({ pokemon, opponent, move }: PreDefendModifyDamageAbAttrParams): boolean { + // TODO: Investigate whether the substitute check can be removed, as it should be accounted for in the move effect phase + return this.condition(pokemon, opponent, move) && !move.hitsSubstitute(opponent, pokemon); } /** * Applies the pre-defense ability to the Pokémon. * Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form. - * - * @param pokemon The Pokémon with the ability. - * @param _passive n/a - * @param _attacker The attacking Pokémon. - * @param _move The move being used. - * @param _cancelled n/a - * @param args Additional arguments. */ - override applyPreDefend( - pokemon: Pokemon, - _passive: boolean, - simulated: boolean, - _attacker: Pokemon, - _move: Move, - _cancelled: BooleanHolder, - args: any[], - ): void { + override apply({ pokemon, simulated, damage }: PreDefendModifyDamageAbAttrParams): void { if (!simulated) { - (args[0] as NumberHolder).value = this.multiplier; + damage.value = this.multiplier; pokemon.removeTag(this.tagType); if (this.recoilDamageFunc) { pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), { @@ -7076,12 +5848,9 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { /** * Gets the message triggered when the Pokémon avoids damage using the form-changing ability. - * @param pokemon The Pokémon with the ability. - * @param abilityName The name of the ability. - * @param _args n/a * @returns The trigger message. */ - getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: PreDefendModifyDamageAbAttrParams, abilityName: string): string { return this.triggerMessageFunc(pokemon, abilityName); } } @@ -7092,23 +5861,22 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @see {@linkcode applyPreSummon()} */ export class PreSummonAbAttr extends AbAttr { - applyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): void {} + private declare readonly _: never; + apply(_params: Closed): void {} - canApplyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { + canApply(_params: Closed): boolean { return true; } } +/** @sealed */ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { /** * Apply a new illusion when summoning Zoroark if the illusion is available * * @param pokemon - The Pokémon with the Illusion ability. - * @param _passive - N/A - * @param _args - N/A - * @returns Whether the illusion was applied. */ - override applyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): void { + override apply({ pokemon }: AbAttrBaseParams): void { const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( p => p.isAllowedInBattle(), ); @@ -7116,7 +5884,8 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { pokemon.setIllusion(lastPokemon); } - override canApplyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { + /** @returns Whether the illusion can be applied. */ + override canApply({ pokemon }: AbAttrBaseParams): boolean { if (pokemon.hasTrainer()) { const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( p => p.isAllowedInBattle(), @@ -7138,53 +5907,28 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } } +/** @sealed */ export class IllusionBreakAbAttr extends AbAttr { - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder | null, - _args: any[], - ): void { + private declare readonly _: never; + // TODO: Consider adding a `canApply` method that checks if the pokemon has an active illusion + override apply({ pokemon }: AbAttrBaseParams): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } } +/** @sealed */ export class PostDefendIllusionBreakAbAttr extends PostDefendAbAttr { /** * Destroy the illusion upon taking damage - * - * @param pokemon - The Pokémon with the Illusion ability. - * @param _passive - unused - * @param _attacker - The attacking Pokémon. - * @param _move - The move being used. - * @param _hitResult - The type of hitResult the pokemon got - * @param _args - unused * @returns - Whether the illusion was destroyed. */ - override applyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - _hitResult: HitResult, - _args: any[], - ): void { + override apply({ pokemon }: PostMoveInteractionAbAttrParams): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } - override canApplyPostDefend( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _attacker: Pokemon, - _move: Move, - hitResult: HitResult, - _args: any[], - ): boolean { + override canApply({ pokemon, hitResult }: PostMoveInteractionAbAttrParams): boolean { const breakIllusion: HitResult[] = [ HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, @@ -7204,121 +5948,108 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { * @param _args - Unused * @returns - Whether the illusion was applied. */ - override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + override apply({ pokemon }: PostBattleAbAttrParams): void { pokemon.breakIllusion(); } } +export interface BypassSpeedChanceAbAttrParams extends AbAttrBaseParams { + /** Holds whether the speed check is bypassed after ability application */ + bypass: BooleanHolder; +} + /** * If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection). - * - * @extends AbAttr + * @sealed */ export class BypassSpeedChanceAbAttr extends AbAttr { public chance: number; /** - * @param {number} chance probability of ability being active. + * @param chance - Probability of the ability activating */ constructor(chance: number) { super(true); this.chance = chance; } - override canApply(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { - const bypassSpeed = args[0] as BooleanHolder; + override canApply({ bypass, simulated, pokemon }: BypassSpeedChanceAbAttrParams): boolean { + // TODO: Consider whether we can move the simulated check to the `apply` method + // May be difficult as we likely do not want to modify the randBattleSeed const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; return ( - !simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove + !simulated && !bypass.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove ); } /** * bypass move order in their priority bracket when pokemon choose damaging move - * @param {Pokemon} _pokemon {@linkcode Pokemon} the Pokemon applying this ability - * @param {boolean} _passive N/A - * @param {BooleanHolder} _cancelled N/A - * @param {any[]} args [0] {@linkcode BooleanHolder} set to true when the ability activated */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const bypassSpeed = args[0] as BooleanHolder; - bypassSpeed.value = true; + override apply({ bypass }: BypassSpeedChanceAbAttrParams): void { + bypass.value = true; } - getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { + override getTriggerMessage({ pokemon }: BypassSpeedChanceAbAttrParams, _abilityName: string): string { return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) }); } } +export interface PreventBypassSpeedChanceAbAttrParams extends AbAttrBaseParams { + /** Holds whether the speed check is bypassed after ability application */ + bypass: BooleanHolder; + /** Holds whether the Pokemon can check held items for Quick Claw's effects */ + canCheckHeldItems: BooleanHolder; +} + /** * This attribute checks if a Pokemon's move meets a provided condition to determine if the Pokemon can use Quick Claw * It was created because Pokemon with the ability Mycelium Might cannot access Quick Claw's benefits when using status moves. + * @sealed */ export class PreventBypassSpeedChanceAbAttr extends AbAttr { private condition: (pokemon: Pokemon, move: Move) => boolean; /** - * @param {function} condition - checks if a move meets certain conditions + * @param condition - checks if a move meets certain conditions */ constructor(condition: (pokemon: Pokemon, move: Move) => boolean) { super(true); this.condition = condition; } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: PreventBypassSpeedChanceAbAttrParams): boolean { + // TODO: Consider having these be passed as parameters instead of being retrieved here const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; return isCommandFight && this.condition(pokemon, move!); } - /** - * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment - * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not - */ - override apply( - _pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - args: any[], - ): void { - const bypassSpeed = args[0] as BooleanHolder; - const canCheckHeldItems = args[1] as BooleanHolder; - bypassSpeed.value = false; + override apply({ bypass, canCheckHeldItems }: PreventBypassSpeedChanceAbAttrParams): void { + bypass.value = false; canCheckHeldItems.value = false; } } +// Also consider making this a postTerrainChange attribute instead of a post-summon attribute /** * This applies a terrain-based type change to the Pokemon. * Used by Mimicry. + * @sealed */ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { constructor() { super(true); } - override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApply({ pokemon }: AbAttrBaseParams): boolean { return !pokemon.isTerastallized; } - override apply( - pokemon: Pokemon, - _passive: boolean, - _simulated: boolean, - _cancelled: BooleanHolder, - _args: any[], - ): void { + override apply({ pokemon }: AbAttrBaseParams): void { const currentTerrain = globalScene.arena.getTerrainType(); const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain); if (typeChange.length !== 0) { @@ -7360,18 +6091,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { return typeChange; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return globalScene.arena.getTerrainType() !== TerrainType.NONE && this.canApply(pokemon, passive, simulated, args); - } - - /** - * Checks if the Pokemon should change types if summoned into an active terrain - */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { - this.apply(pokemon, passive, simulated, new BooleanHolder(false), []); - } - - override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]) { + override getTriggerMessage({ pokemon }: AbAttrBaseParams, _abilityName: string) { const currentTerrain = globalScene.arena.getTerrainType(); const pokemonNameWithAffix = getPokemonNameWithAffix(pokemon); if (currentTerrain === TerrainType.NONE) { @@ -7494,7 +6214,7 @@ class ForceSwitchOutHelper { if (player) { const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", opponent, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: opponent, cancelled: blockedByAbility }); return !blockedByAbility.value; } @@ -7532,7 +6252,7 @@ class ForceSwitchOutHelper { */ public getFailedText(target: Pokemon): string | null { const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: target, cancelled: blockedByAbility }); return blockedByAbility.value ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) : null; @@ -7557,30 +6277,21 @@ function calculateShellBellRecovery(pokemon: Pokemon): number { return 0; } +export interface PostDamageAbAttrParams extends AbAttrBaseParams { + /** The pokemon that caused the damage; omitted if the damage was not from a pokemon */ + source?: Pokemon; + /** The amount of damage that was dealt */ + readonly damage: number; +} /** * Triggers after the Pokemon takes any damage - * @extends AbAttr */ export class PostDamageAbAttr extends AbAttr { - public canApplyPostDamage( - _pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): boolean { + override canApply(_params: PostDamageAbAttrParams): boolean { return true; } - public applyPostDamage( - _pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): void {} + override apply(_params: PostDamageAbAttrParams): void {} } /** @@ -7590,8 +6301,8 @@ export class PostDamageAbAttr extends AbAttr { * * Used by Wimp Out and Emergency Exit * - * @extends PostDamageAbAttr * @see {@linkcode applyPostDamage} + * @sealed */ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { private helper: ForceSwitchOutHelper = new ForceSwitchOutHelper(SwitchType.SWITCH); @@ -7603,14 +6314,7 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { } // TODO: Refactor to use more early returns - public override canApplyPostDamage( - pokemon: Pokemon, - damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - source?: Pokemon, - ): boolean { + public override canApply({ pokemon, source, damage }: PostDamageAbAttrParams): boolean { const moveHistory = pokemon.getMoveHistory(); // Will not activate when the Pokémon's HP is lowered by cutting its own HP const fordbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT]; @@ -7668,22 +6372,9 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { * Applies the switch-out logic after the Pokémon takes damage. * Checks various conditions based on the moves used by the Pokémon, the opponents' moves, and * the Pokémon's health after damage to determine whether the switch-out should occur. - * - * @param pokemon The Pokémon that took damage. - * @param _damage N/A - * @param _passive N/A - * @param _simulated Whether the ability is being simulated. - * @param _args N/A - * @param _source N/A */ - public override applyPostDamage( - pokemon: Pokemon, - _damage: number, - _passive: boolean, - _simulated: boolean, - _args: any[], - _source?: Pokemon, - ): void { + public override apply({ pokemon }: PostDamageAbAttrParams): void { + // TODO: Consider respecting the `simulated` flag here this.helper.switchOutLogic(pokemon); } } @@ -7844,7 +6535,8 @@ const AbilityAttrs = Object.freeze({ PostTurnStatusHealAbAttr, PostTurnResetStatusAbAttr, PostTurnRestoreBerryAbAttr, - RepeatBerryNextTurnAbAttr, + CudChewConsumeBerryAbAttr, + CudChewRecordBerryAbAttr, MoodyAbAttr, SpeedBoostAbAttr, PostTurnHealAbAttr, @@ -8965,7 +7657,8 @@ export function initAbilities() { new Ability(AbilityId.OPPORTUNIST, 9) .attr(StatStageChangeCopyAbAttr), new Ability(AbilityId.CUD_CHEW, 9) - .attr(RepeatBerryNextTurnAbAttr), + .attr(CudChewConsumeBerryAbAttr) + .attr(CudChewRecordBerryAbAttr), new Ability(AbilityId.SHARPNESS, 9) .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), new Ability(AbilityId.SUPREME_OVERLORD, 9) diff --git a/src/data/abilities/apply-ab-attrs.ts b/src/data/abilities/apply-ab-attrs.ts index fdbd2652698..1571d64d170 100644 --- a/src/data/abilities/apply-ab-attrs.ts +++ b/src/data/abilities/apply-ab-attrs.ts @@ -1,63 +1,14 @@ -import type { AbAttrApplyFunc, AbAttrMap, AbAttrString, AbAttrSuccessFunc } from "#app/@types/ability-types"; -import type Pokemon from "#app/field/pokemon"; +import type { AbAttrParamMap } from "#app/@types/ability-types"; +import type { AbAttrBaseParams, AbAttrString, CallableAbAttrString } from "#app/@types/ability-types"; import { globalScene } from "#app/global-scene"; -import type { BooleanHolder, NumberHolder } from "#app/utils/common"; -import type { BattlerIndex } from "#enums/battler-index"; -import type { HitResult } from "#enums/hit-result"; -import type { BattleStat, Stat } from "#enums/stat"; -import type { StatusEffect } from "#enums/status-effect"; -import type { WeatherType } from "#enums/weather-type"; -import type { BattlerTag } from "../battler-tags"; -import type Move from "../moves/move"; -import type { PokemonMove } from "../moves/pokemon-move"; -import type { TerrainType } from "../terrain"; -import type { Weather } from "../weather"; -import type { - PostBattleInitAbAttr, - PreDefendAbAttr, - PostDefendAbAttr, - PostMoveUsedAbAttr, - StatMultiplierAbAttr, - AllyStatMultiplierAbAttr, - PostSetStatusAbAttr, - PostDamageAbAttr, - FieldMultiplyStatAbAttr, - PreAttackAbAttr, - ExecutedMoveAbAttr, - PostAttackAbAttr, - PostKnockOutAbAttr, - PostVictoryAbAttr, - PostSummonAbAttr, - PreSummonAbAttr, - PreSwitchOutAbAttr, - PreLeaveFieldAbAttr, - PreStatStageChangeAbAttr, - PostStatStageChangeAbAttr, - PreSetStatusAbAttr, - PreApplyBattlerTagAbAttr, - PreWeatherEffectAbAttr, - PreWeatherDamageAbAttr, - PostTurnAbAttr, - PostWeatherChangeAbAttr, - PostWeatherLapseAbAttr, - PostTerrainChangeAbAttr, - CheckTrappedAbAttr, - PostBattleAbAttr, - PostFaintAbAttr, - PostItemLostAbAttr, -} from "./ability"; function applySingleAbAttrs( - pokemon: Pokemon, - passive: boolean, attrType: T, - applyFunc: AbAttrApplyFunc, - successFunc: AbAttrSuccessFunc, - args: any[], + params: AbAttrParamMap[T], gainedMidTurn = false, - simulated = false, messages: string[] = [], ) { + const { simulated = false, passive = false, pokemon } = params; if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) { return; } @@ -75,7 +26,11 @@ function applySingleAbAttrs( for (const attr of ability.getAttrs(attrType)) { const condition = attr.getCondition(); let abShown = false; - if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) { + // We require an `as any` cast to suppress an error about the `params` type not being assignable to + // the type of the argument expected by `attr.canApply()`. This is OK, because we know that + // `attr` is an instance of the `attrType` class provided to the method, and typescript _will_ check + // that the `params` object has the correct properties for that class at the callsites. + if ((condition && !condition(pokemon)) || !attr.canApply(params as any)) { continue; } @@ -85,15 +40,16 @@ function applySingleAbAttrs( globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true); abShown = true; } - const message = attr.getTriggerMessage(pokemon, ability.name, args); + + const message = attr.getTriggerMessage(params as any, ability.name); if (message) { if (!simulated) { globalScene.phaseManager.queueMessage(message); } messages.push(message); } - - applyFunc(attr, passive); + // The `as any` cast here uses the same reasoning as above. + attr.apply(params as any); if (abShown) { globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false); @@ -107,726 +63,60 @@ function applySingleAbAttrs( } } -function applyAbAttrsInternal( +function applyAbAttrsInternal( attrType: T, - pokemon: Pokemon | null, - applyFunc: AbAttrApplyFunc, - successFunc: AbAttrSuccessFunc, - args: any[], - simulated = false, + params: AbAttrParamMap[T], messages: string[] = [], gainedMidTurn = false, ) { - for (const passive of [false, true]) { - if (pokemon) { - applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages); - globalScene.phaseManager.clearPhaseQueueSplice(); - } + // If the pokemon is not defined, no ability attributes to be applied. + // TODO: Evaluate whether this check is even necessary anymore + if (!params.pokemon) { + return; } + if (params.passive !== undefined) { + applySingleAbAttrs(attrType, params, gainedMidTurn, messages); + return; + } + for (const passive of [false, true]) { + params.passive = passive; + applySingleAbAttrs(attrType, params, gainedMidTurn, messages); + globalScene.phaseManager.clearPhaseQueueSplice(); + } + // We need to restore passive to its original state in the case that it was undefined on entry + // this is necessary in case this method is called with an object that is reused. + params.passive = undefined; } -export function applyAbAttrs( +/** + * @param attrType - The type of the ability attribute to apply. (note: may not be any attribute that extends PostSummonAbAttr) + * @param params - The parameters to pass to the ability attribute's apply method + * @param messages - An optional array to which ability trigger messges will be added + */ +export function applyAbAttrs( attrType: T, - pokemon: Pokemon, - cancelled: BooleanHolder | null, - simulated = false, - ...args: any[] + params: AbAttrParamMap[T], + messages?: string[], ): void { - applyAbAttrsInternal( - attrType, - pokemon, - // @ts-expect-error: TODO: fix the error on `cancelled` - (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), - (attr, passive) => attr.canApply(pokemon, passive, simulated, args), - args, - simulated, - ); + applyAbAttrsInternal(attrType, params, messages); } // TODO: Improve the type signatures of the following methods / refactor the apply methods -export function applyPostBattleInitAbAttrs( - attrType: AbAttrMap[K] extends PostBattleInitAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostBattleInitAbAttr).applyPostBattleInit(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostBattleInitAbAttr).canApplyPostBattleInit(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreDefendAbAttrs( - attrType: AbAttrMap[K] extends PreDefendAbAttr ? K : never, - pokemon: Pokemon, - attacker: Pokemon, - move: Move | null, - cancelled: BooleanHolder | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreDefendAbAttr).applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), - (attr, passive) => - (attr as PreDefendAbAttr).canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), - args, - simulated, - ); -} - -export function applyPostDefendAbAttrs( - attrType: AbAttrMap[K] extends PostDefendAbAttr ? K : never, - pokemon: Pokemon, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostDefendAbAttr).applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - (attr, passive) => - (attr as PostDefendAbAttr).canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostMoveUsedAbAttrs( - attrType: AbAttrMap[K] extends PostMoveUsedAbAttr ? K : never, - pokemon: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => (attr as PostMoveUsedAbAttr).applyPostMoveUsed(pokemon, move, source, targets, simulated, args), - (attr, _passive) => - (attr as PostMoveUsedAbAttr).canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args), - args, - simulated, - ); -} - -export function applyStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends StatMultiplierAbAttr ? K : never, - pokemon: Pokemon, - stat: BattleStat, - statValue: NumberHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as StatMultiplierAbAttr).applyStatStage(pokemon, passive, simulated, stat, statValue, args), - (attr, passive) => - (attr as StatMultiplierAbAttr).canApplyStatStage(pokemon, passive, simulated, stat, statValue, args), - args, - ); -} - -/** - * Applies an ally's Stat multiplier attribute - * @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being - * @param pokemon - The {@linkcode Pokemon} with the ability - * @param stat - The type of the checked {@linkcode Stat} - * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param checkedPokemon - The {@linkcode Pokemon} with the checked stat - * @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move. - * @param args - unused - */ -export function applyAllyStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends AllyStatMultiplierAbAttr ? K : never, - pokemon: Pokemon, - stat: BattleStat, - statValue: NumberHolder, - simulated = false, - checkedPokemon: Pokemon, - ignoreAbility: boolean, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as AllyStatMultiplierAbAttr).applyAllyStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - ignoreAbility, - args, - ), - (attr, passive) => - (attr as AllyStatMultiplierAbAttr).canApplyAllyStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - ignoreAbility, - args, - ), - args, - simulated, - ); -} - -export function applyPostSetStatusAbAttrs( - attrType: AbAttrMap[K] extends PostSetStatusAbAttr ? K : never, - pokemon: Pokemon, - effect: StatusEffect, - sourcePokemon?: Pokemon | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostSetStatusAbAttr).applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), - (attr, passive) => - (attr as PostSetStatusAbAttr).canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), - args, - simulated, - ); -} - -export function applyPostDamageAbAttrs( - attrType: AbAttrMap[K] extends PostDamageAbAttr ? K : never, - pokemon: Pokemon, - damage: number, - _passive: boolean, - simulated = false, - args: any[], - source?: Pokemon, -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostDamageAbAttr).applyPostDamage(pokemon, damage, passive, simulated, args, source), - (attr, passive) => (attr as PostDamageAbAttr).canApplyPostDamage(pokemon, damage, passive, simulated, args, source), - args, - ); -} -/** - * Applies a field Stat multiplier attribute - * @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being - * @param pokemon {@linkcode Pokemon} the Pokemon applying this ability - * @param stat {@linkcode Stat} the type of the checked stat - * @param statValue {@linkcode NumberHolder} the value of the checked stat - * @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat - * @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat - * @param args unused - */ - -export function applyFieldStatMultiplierAbAttrs( - attrType: AbAttrMap[K] extends FieldMultiplyStatAbAttr ? K : never, - pokemon: Pokemon, - stat: Stat, - statValue: NumberHolder, - checkedPokemon: Pokemon, - hasApplied: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as FieldMultiplyStatAbAttr).applyFieldStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - hasApplied, - args, - ), - (attr, passive) => - (attr as FieldMultiplyStatAbAttr).canApplyFieldStat( - pokemon, - passive, - simulated, - stat, - statValue, - checkedPokemon, - hasApplied, - args, - ), - args, - ); -} - -export function applyPreAttackAbAttrs( - attrType: AbAttrMap[K] extends PreAttackAbAttr ? K : never, - pokemon: Pokemon, - defender: Pokemon | null, - move: Move, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreAttackAbAttr).applyPreAttack(pokemon, passive, simulated, defender, move, args), - (attr, passive) => (attr as PreAttackAbAttr).canApplyPreAttack(pokemon, passive, simulated, defender, move, args), - args, - simulated, - ); -} - -export function applyExecutedMoveAbAttrs( - attrType: AbAttrMap[K] extends ExecutedMoveAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - attr => (attr as ExecutedMoveAbAttr).applyExecutedMove(pokemon, simulated), - attr => (attr as ExecutedMoveAbAttr).canApplyExecutedMove(pokemon, simulated), - args, - simulated, - ); -} - -export function applyPostAttackAbAttrs( - attrType: AbAttrMap[K] extends PostAttackAbAttr ? K : never, - pokemon: Pokemon, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostAttackAbAttr).applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - (attr, passive) => - (attr as PostAttackAbAttr).canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostKnockOutAbAttrs( - attrType: AbAttrMap[K] extends PostKnockOutAbAttr ? K : never, - pokemon: Pokemon, - knockedOut: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostKnockOutAbAttr).applyPostKnockOut(pokemon, passive, simulated, knockedOut, args), - (attr, passive) => (attr as PostKnockOutAbAttr).canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args), - args, - simulated, - ); -} - -export function applyPostVictoryAbAttrs( - attrType: AbAttrMap[K] extends PostVictoryAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostVictoryAbAttr).applyPostVictory(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostVictoryAbAttr).canApplyPostVictory(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostSummonAbAttrs( - attrType: AbAttrMap[K] extends PostSummonAbAttr ? K : never, - pokemon: Pokemon, - passive = false, - simulated = false, - ...args: any[] -): void { - applySingleAbAttrs( - pokemon, - passive, - attrType, - (attr, passive) => (attr as PostSummonAbAttr).applyPostSummon(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostSummonAbAttr).canApplyPostSummon(pokemon, passive, simulated, args), - args, - false, - simulated, - ); -} - -export function applyPreSummonAbAttrs( - attrType: AbAttrMap[K] extends PreSummonAbAttr ? K : never, - pokemon: Pokemon, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreSummonAbAttr).applyPreSummon(pokemon, passive, args), - (attr, passive) => (attr as PreSummonAbAttr).canApplyPreSummon(pokemon, passive, args), - args, - ); -} - -export function applyPreSwitchOutAbAttrs( - attrType: AbAttrMap[K] extends PreSwitchOutAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreSwitchOutAbAttr).applyPreSwitchOut(pokemon, passive, simulated, args), - (attr, passive) => (attr as PreSwitchOutAbAttr).canApplyPreSwitchOut(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreLeaveFieldAbAttrs( - attrType: AbAttrMap[K] extends PreLeaveFieldAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PreLeaveFieldAbAttr).applyPreLeaveField(pokemon, passive, simulated, args), - (attr, passive) => (attr as PreLeaveFieldAbAttr).canApplyPreLeaveField(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPreStatStageChangeAbAttrs( - attrType: AbAttrMap[K] extends PreStatStageChangeAbAttr ? K : never, - pokemon: Pokemon | null, - stat: BattleStat, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreStatStageChangeAbAttr).applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), - (attr, passive) => - (attr as PreStatStageChangeAbAttr).canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args), - args, - simulated, - ); -} - -export function applyPostStatStageChangeAbAttrs( - attrType: AbAttrMap[K] extends PostStatStageChangeAbAttr ? K : never, - pokemon: Pokemon, - stats: BattleStat[], - stages: number, - selfTarget: boolean, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => - (attr as PostStatStageChangeAbAttr).applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), - (attr, _passive) => - (attr as PostStatStageChangeAbAttr).canApplyPostStatStageChange( - pokemon, - simulated, - stats, - stages, - selfTarget, - args, - ), - args, - simulated, - ); -} - -export function applyPreSetStatusAbAttrs( - attrType: AbAttrMap[K] extends PreSetStatusAbAttr ? K : never, - pokemon: Pokemon, - effect: StatusEffect | undefined, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreSetStatusAbAttr).applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), - (attr, passive) => - (attr as PreSetStatusAbAttr).canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), - args, - simulated, - ); -} - -export function applyPreApplyBattlerTagAbAttrs( - attrType: AbAttrMap[K] extends PreApplyBattlerTagAbAttr ? K : never, - pokemon: Pokemon, - tag: BattlerTag, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreApplyBattlerTagAbAttr).applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), - (attr, passive) => - (attr as PreApplyBattlerTagAbAttr).canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), - args, - simulated, - ); -} - -export function applyPreWeatherEffectAbAttrs( - attrType: AbAttrMap[K] extends PreWeatherEffectAbAttr ? K : never, - pokemon: Pokemon, - weather: Weather | null, - cancelled: BooleanHolder, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PreWeatherDamageAbAttr).applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), - (attr, passive) => - (attr as PreWeatherDamageAbAttr).canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), - args, - simulated, - ); -} - -export function applyPostTurnAbAttrs( - attrType: AbAttrMap[K] extends PostTurnAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostTurnAbAttr).applyPostTurn(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostTurnAbAttr).canApplyPostTurn(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostWeatherChangeAbAttrs( - attrType: AbAttrMap[K] extends PostWeatherChangeAbAttr ? K : never, - pokemon: Pokemon, - weather: WeatherType, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostWeatherChangeAbAttr).applyPostWeatherChange(pokemon, passive, simulated, weather, args), - (attr, passive) => - (attr as PostWeatherChangeAbAttr).canApplyPostWeatherChange(pokemon, passive, simulated, weather, args), - args, - simulated, - ); -} - -export function applyPostWeatherLapseAbAttrs( - attrType: AbAttrMap[K] extends PostWeatherLapseAbAttr ? K : never, - pokemon: Pokemon, - weather: Weather | null, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostWeatherLapseAbAttr).applyPostWeatherLapse(pokemon, passive, simulated, weather, args), - (attr, passive) => - (attr as PostWeatherLapseAbAttr).canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args), - args, - simulated, - ); -} - -export function applyPostTerrainChangeAbAttrs( - attrType: AbAttrMap[K] extends PostTerrainChangeAbAttr ? K : never, - pokemon: Pokemon, - terrain: TerrainType, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostTerrainChangeAbAttr).applyPostTerrainChange(pokemon, passive, simulated, terrain, args), - (attr, passive) => - (attr as PostTerrainChangeAbAttr).canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args), - args, - simulated, - ); -} - -export function applyCheckTrappedAbAttrs( - attrType: AbAttrMap[K] extends CheckTrappedAbAttr ? K : never, - pokemon: Pokemon, - trapped: BooleanHolder, - otherPokemon: Pokemon, - messages: string[], - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as CheckTrappedAbAttr).applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - (attr, passive) => - (attr as CheckTrappedAbAttr).canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - args, - simulated, - messages, - ); -} - -export function applyPostBattleAbAttrs( - attrType: AbAttrMap[K] extends PostBattleAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => (attr as PostBattleAbAttr).applyPostBattle(pokemon, passive, simulated, args), - (attr, passive) => (attr as PostBattleAbAttr).canApplyPostBattle(pokemon, passive, simulated, args), - args, - simulated, - ); -} - -export function applyPostFaintAbAttrs( - attrType: AbAttrMap[K] extends PostFaintAbAttr ? K : never, - pokemon: Pokemon, - attacker?: Pokemon, - move?: Move, - hitResult?: HitResult, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, passive) => - (attr as PostFaintAbAttr).applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), - (attr, passive) => - (attr as PostFaintAbAttr).canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), - args, - simulated, - ); -} - -export function applyPostItemLostAbAttrs( - attrType: AbAttrMap[K] extends PostItemLostAbAttr ? K : never, - pokemon: Pokemon, - simulated = false, - ...args: any[] -): void { - applyAbAttrsInternal( - attrType, - pokemon, - (attr, _passive) => (attr as PostItemLostAbAttr).applyPostItemLost(pokemon, simulated, args), - (attr, _passive) => (attr as PostItemLostAbAttr).canApplyPostItemLost(pokemon, simulated, args), - args, - ); -} - /** * Applies abilities when they become active mid-turn (ability switch) * * Ignores passives as they don't change and shouldn't be reapplied when main abilities change */ -export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs( - pokemon, - passive, - "PostSummonAbAttr", - (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), - (attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args), - args, - true, - simulated, - ); +export function applyOnGainAbAttrs(params: AbAttrBaseParams): void { + applySingleAbAttrs("PostSummonAbAttr", params, true); } + /** * Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather) */ -export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { - applySingleAbAttrs( - pokemon, - passive, - "PreLeaveFieldAbAttr", - (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]), - (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]), - args, - true, - simulated, - ); +export function applyOnLoseAbAttrs(params: AbAttrBaseParams): void { + applySingleAbAttrs("PreLeaveFieldAbAttr", params, true); - applySingleAbAttrs( - pokemon, - passive, - "IllusionBreakAbAttr", - (attr, passive) => attr.apply(pokemon, passive, simulated, null, args), - (attr, passive) => attr.canApply(pokemon, passive, simulated, args), - args, - true, - simulated, - ); + applySingleAbAttrs("IllusionBreakAbAttr", params, true); } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 494a0438b18..48fdebd745f 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -72,10 +72,11 @@ export abstract class ArenaTag { /** * Helper function that retrieves the source Pokemon - * @returns The source {@linkcode Pokemon} or `null` if none is found + * @returns - The source {@linkcode Pokemon} for this tag. + * Returns `null` if `this.sourceId` is `undefined` */ public getSourcePokemon(): Pokemon | null { - return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + return globalScene.getPokemonById(this.sourceId); } /** @@ -107,19 +108,22 @@ export class MistTag extends ArenaTag { onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - if (this.sourceId) { - const source = globalScene.getPokemonById(this.sourceId); - - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:mistOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); - } else if (!quiet) { - console.warn("Failed to get source for MistTag onAdd"); - } + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for MistTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:mistOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } /** @@ -137,7 +141,7 @@ export class MistTag extends ArenaTag { if (attacker) { const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, simulated: false, bypassed }); if (bypassed.value) { return false; } @@ -202,7 +206,7 @@ export class WeakenMoveScreenTag extends ArenaTag { ): boolean { if (this.weakenedCategories.includes(moveCategory)) { const bypassed = new BooleanHolder(false); - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed }); if (bypassed.value) { return false; } @@ -440,18 +444,18 @@ class MatBlockTag extends ConditionalProtectTag { } onAdd(_arena: Arena) { - if (this.sourceId) { - const source = globalScene.getPokemonById(this.sourceId); - if (source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:matBlockOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); - } else { - console.warn("Failed to get source for MatBlockTag onAdd"); - } + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for Mat Block message; id: ${this.sourceId}`); + return; } + + super.onAdd(_arena); + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:matBlockOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } } @@ -511,7 +515,12 @@ export class NoCritTag extends ArenaTag { /** Queues a message upon removing this effect from the field */ onRemove(_arena: Arena): void { - const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct? + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for NoCritTag on remove message; id: ${this.sourceId}`); + return; + } + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:noCritOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), @@ -522,7 +531,7 @@ export class NoCritTag extends ArenaTag { } /** - * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) Wish}. + * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) | Wish}. * Heals the Pokémon in the user's position the turn after Wish is used. */ class WishTag extends ArenaTag { @@ -535,18 +544,20 @@ class WishTag extends ArenaTag { } onAdd(_arena: Arena): void { - if (this.sourceId) { - const user = globalScene.getPokemonById(this.sourceId); - if (user) { - this.battlerIndex = user.getBattlerIndex(); - this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(user), - }); - this.healHp = toDmgValue(user.getMaxHp() / 2); - } else { - console.warn("Failed to get source for WishTag onAdd"); - } + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for WishTag on add message; id: ${this.sourceId}`); + return; } + + super.onAdd(_arena); + this.healHp = toDmgValue(source.getMaxHp() / 2); + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:wishTagOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } onRemove(_arena: Arena): void { @@ -741,15 +752,23 @@ class SpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:spikesOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:spikesOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { @@ -758,7 +777,7 @@ class SpikesTag extends ArenaTrapTag { } const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (simulated || cancelled.value) { return !cancelled.value; } @@ -794,15 +813,23 @@ class ToxicSpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:toxicSpikesOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + if (quiet) { + // We assume `quiet=true` means "just add the bloody tag no questions asked" + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ToxicSpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:toxicSpikesOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } onRemove(arena: Arena): void { @@ -905,7 +932,11 @@ class StealthRockTag extends ArenaTrapTag { onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + if (quiet) { + return; + } + + const source = this.getSourcePokemon(); if (!quiet && source) { globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stealthRockOnAdd", { @@ -946,7 +977,7 @@ class StealthRockTag extends ArenaTrapTag { override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (cancelled.value) { return false; } @@ -989,21 +1020,35 @@ class StickyWebTag extends ArenaTrapTag { onAdd(arena: Arena, quiet = false): void { super.onAdd(arena); - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!quiet && source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:stickyWebOnAdd", { - moveName: this.getMoveName(), - opponentDesc: source.getOpponentDescriptor(), - }), - ); + + // We assume `quiet=true` means "just add the bloody tag no questions asked" + if (quiet) { + return; } + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SpikesTag on add message; id: ${this.sourceId}`); + return; + } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:stickyWebOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { if (pokemon.isGrounded()) { const cancelled = new BooleanHolder(false); - applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled); + applyAbAttrs("ProtectStatAbAttr", { + pokemon, + cancelled, + stat: Stat.SPD, + stages: -1, + }); if (simulated) { return !cancelled.value; @@ -1061,14 +1106,20 @@ export class TrickRoomTag extends ArenaTag { } onAdd(_arena: Arena): void { - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (source) { - globalScene.phaseManager.queueMessage( - i18next.t("arenaTag:trickRoomOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); + super.onAdd(_arena); + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for TrickRoomTag on add message; id: ${this.sourceId}`); + return; } + + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:trickRoomOnAdd", { + moveName: this.getMoveName(), + opponentDesc: source.getOpponentDescriptor(), + }), + ); } onRemove(_arena: Arena): void { @@ -1115,6 +1166,13 @@ class TailwindTag extends ArenaTag { } onAdd(_arena: Arena, quiet = false): void { + const source = this.getSourcePokemon(); + if (!source) { + return; + } + + super.onAdd(_arena, quiet); + if (!quiet) { globalScene.phaseManager.queueMessage( i18next.t( @@ -1123,15 +1181,14 @@ class TailwindTag extends ArenaTag { ); } - const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! - const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? []; - const phaseManager = globalScene.phaseManager; + const field = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); - for (const pokemon of party) { + for (const pokemon of field) { // Apply the CHARGED tag to party members with the WIND_POWER ability + // TODO: This should not be handled here if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { pokemon.addTag(BattlerTagType.CHARGED); - phaseManager.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -1142,9 +1199,16 @@ class TailwindTag extends ArenaTag { // Raise attack by one stage if party member has WIND_RIDER ability // TODO: Ability displays should be handled by the ability if (pokemon.hasAbility(AbilityId.WIND_RIDER)) { - phaseManager.queueAbilityDisplay(pokemon, false, true); - phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true); - phaseManager.queueAbilityDisplay(pokemon, false, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, true); + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.ATK], + 1, + true, + ); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, false); } } } @@ -1216,24 +1280,26 @@ class ImprisonTag extends ArenaTrapTag { } /** - * This function applies the effects of Imprison to the opposing Pokemon already present on the field. - * @param arena + * Apply the effects of Imprison to all opposing on-field Pokemon. */ override onAdd() { const source = this.getSourcePokemon(); - if (source) { - const party = this.getAffectedPokemon(); - party?.forEach((p: Pokemon) => { - if (p.isAllowedInBattle()) { - p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId); - } - }); - globalScene.phaseManager.queueMessage( - i18next.t("battlerTags:imprisonOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), - }), - ); + if (!source) { + return; } + + const party = this.getAffectedPokemon(); + party.forEach(p => { + if (p.isAllowedInBattle()) { + p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId); + } + }); + + globalScene.phaseManager.queueMessage( + i18next.t("battlerTags:imprisonOnAdd", { + pokemonNameWithAffix: getPokemonNameWithAffix(source), + }), + ); } /** @@ -1243,7 +1309,7 @@ class ImprisonTag extends ArenaTrapTag { */ override lapse(): boolean { const source = this.getSourcePokemon(); - return source ? source.isActive(true) : false; + return !!source?.isActive(true); } /** @@ -1265,9 +1331,7 @@ class ImprisonTag extends ArenaTrapTag { */ override onRemove(): void { const party = this.getAffectedPokemon(); - party?.forEach((p: Pokemon) => { - p.removeTag(BattlerTagType.IMPRISON); - }); + party.forEach(p => p.removeTag(BattlerTagType.IMPRISON)); } } @@ -1416,7 +1480,9 @@ export class SuppressAbilitiesTag extends ArenaTag { for (const fieldPokemon of globalScene.getField(true)) { if (fieldPokemon && fieldPokemon.id !== pokemon.id) { - [true, false].forEach(passive => applyOnLoseAbAttrs(fieldPokemon, passive)); + // TODO: investigate whether we can just remove the foreach and call `applyAbAttrs` directly, providing + // the appropriate attributes (preLEaveField and IllusionBreak) + [true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: fieldPokemon, passive })); } } } @@ -1438,7 +1504,10 @@ export class SuppressAbilitiesTag extends ArenaTag { const setter = globalScene .getField() .filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0]; - applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr")); + applyOnGainAbAttrs({ + pokemon: setter, + passive: setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"), + }); } } @@ -1451,7 +1520,7 @@ export class SuppressAbilitiesTag extends ArenaTag { for (const pokemon of globalScene.getField(true)) { // There is only one pokemon with this attr on the field on removal, so its abilities are already active if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) { - [true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive)); + [true, false].forEach(passive => applyOnGainAbAttrs({ pokemon, passive })); } } } diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 436e6bc6e76..fa89e558ba7 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -595,13 +595,13 @@ function parseEggMoves(content: string): void { const cols = line.split(",").slice(0, 5); const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, "").toLowerCase()); const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, "_"); - const species = speciesValues[speciesNames.findIndex(s => s === enumSpeciesName)]; + const species = speciesValues[speciesNames.indexOf(enumSpeciesName)]; const eggMoves: MoveId[] = []; for (let m = 0; m < 4; m++) { const moveName = cols[m + 1].trim(); - const moveIndex = moveName !== "N/A" ? moveNames.findIndex(mn => mn === moveName.toLowerCase()) : -1; + const moveIndex = moveName !== "N/A" ? moveNames.indexOf(moveName.toLowerCase()) : -1; eggMoves.push(moveIndex > -1 ? moveIndex as MoveId : MoveId.NONE); if (moveIndex === -1) { diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 5dda1912e44..8714d6c0c9a 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -650,8 +650,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.KIRLIA, 20, null, null) ], [SpeciesId.KIRLIA]: [ - new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}), - new SpeciesEvolution(SpeciesId.GALLADE, 30, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}) + new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null), + new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}) ], [SpeciesId.SURSKIT]: [ new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null) @@ -739,8 +739,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.DUSCLOPS, 37, null, null) ], [SpeciesId.SNORUNT]: [ - new SpeciesEvolution(SpeciesId.GLALIE, 42, null, {key: EvoCondKey.GENDER, gender: Gender.MALE}), - new SpeciesEvolution(SpeciesId.FROSLASS, 42, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}) + new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null), + new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}) ], [SpeciesId.SPHEAL]: [ new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index be060b57e9c..bed17fb0ebc 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -346,7 +346,7 @@ abstract class AnimTimedBgEvent extends AnimTimedEvent { } class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { - // biome-ignore lint/correctness/noUnusedVariables: seems intentional + // biome-ignore lint/correctness/noUnusedFunctionParameters: seems intentional execute(moveAnim: MoveAnim, priority?: number): number { const tweenProps = {}; if (this.bgX !== undefined) { @@ -359,15 +359,11 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent { tweenProps["alpha"] = (this.opacity || 0) / 255; } if (Object.keys(tweenProps).length) { - globalScene.tweens.add( - Object.assign( - { - targets: moveAnim.bgSprite, - duration: getFrameMs(this.duration * 3), - }, - tweenProps, - ), - ); + globalScene.tweens.add({ + targets: moveAnim.bgSprite, + duration: getFrameMs(this.duration * 3), + ...tweenProps, + }); } return this.duration * 2; } @@ -423,7 +419,7 @@ export function initCommonAnims(): Promise { const commonAnimId = commonAnimIds[ca]; commonAnimFetches.push( globalScene - .cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))), ); @@ -535,7 +531,7 @@ export async function initEncounterAnims(encounterAnim: EncounterAnim | Encounte } encounterAnimFetches.push( globalScene - .cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(cas => encounterAnims.set(anim, new AnimConfig(cas))), ); @@ -559,7 +555,7 @@ export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise { } else { chargeAnims.set(chargeAnim, null); globalScene - .cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`) + .cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/_/g, "-")}.json`) .then(response => response.json()) .then(ca => { if (Array.isArray(ca)) { @@ -1405,15 +1401,15 @@ export class EncounterBattleAnim extends BattleAnim { export async function populateAnims() { const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase()); - const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, "")); + const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/_/g, "")); const commonAnimIds = getEnumValues(CommonAnim) as CommonAnim[]; const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase()); - const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, " ")); + const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/_/g, " ")); const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[]; const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/; const moveNameToId = {}; for (const move of getEnumValues(MoveId).slice(1)) { - const moveName = MoveId[move].toUpperCase().replace(/\_/g, ""); + const moveName = MoveId[move].toUpperCase().replace(/_/g, ""); moveNameToId[moveName] = move; } @@ -1469,7 +1465,7 @@ export async function populateAnims() { const frameData = framesData[fd]; const focusFramesData = frameData.split(" - - "); for (let tf = 0; tf < focusFramesData.length; tf++) { - const values = focusFramesData[tf].replace(/ {6}\- /g, "").split("\n"); + const values = focusFramesData[tf].replace(/ {6}- /g, "").split("\n"); const targetFrame = new AnimFrame( Number.parseFloat(values[0]), Number.parseFloat(values[1]), @@ -1516,7 +1512,7 @@ export async function populateAnims() { .replace(/[a-z]+: ! '', /gi, "") .replace(/name: (.*?),/, 'name: "$1",') .replace( - /flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, + /flashColor: !ruby\/object:Color { alpha: ([\d.]+), blue: ([\d.]+), green: ([\d.]+), red: ([\d.]+)}/, "flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1", ); const frameIndex = Number.parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct? @@ -1641,12 +1637,12 @@ export async function populateAnims() { let props: string[]; for (let p = 0; p < propSets.length; p++) { props = propSets[p]; - // @ts-ignore TODO + // @ts-expect-error TODO const ai = props.indexOf(a.key); if (ai === -1) { continue; } - // @ts-ignore TODO + // @ts-expect-error TODO const bi = props.indexOf(b.key); return ai < bi ? -1 : ai > bi ? 1 : 0; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 8405fd1dd4d..7e9b9825e06 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -111,7 +111,7 @@ export class BattlerTag { * @returns The source {@linkcode Pokemon}, or `null` if none is found */ public getSourcePokemon(): Pokemon | null { - return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; + return globalScene.getPokemonById(this.sourceId); } } @@ -540,9 +540,13 @@ export class TrappedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - const source = globalScene.getPokemonById(this.sourceId!)!; - const move = allMoves[this.sourceMove]; + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for TrappedTag canAdd; id: ${this.sourceId}`); + return false; + } + const move = allMoves[this.sourceMove]; const isGhost = pokemon.isOfType(PokemonType.GHOST); const isTrapped = pokemon.getTag(TrappedTag); const hasSubstitute = move.hitsSubstitute(source, pokemon); @@ -621,7 +625,7 @@ export class FlinchedTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - applyAbAttrs("FlinchEffectAbAttr", pokemon, null); + applyAbAttrs("FlinchEffectAbAttr", { pokemon }); return true; } @@ -763,12 +767,20 @@ export class DestinyBondTag extends BattlerTag { if (lapseType !== BattlerTagLapseType.CUSTOM) { return super.lapse(pokemon, lapseType); } - const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; - if (!source?.isFainted()) { + + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for DestinyBondTag lapse; id: ${this.sourceId}`); + return false; + } + + // Destiny bond stays active until the user faints + if (!source.isFainted()) { return true; } - if (source?.getAlly() === pokemon) { + // Don't kill allies or opposing bosses. + if (source.getAlly() === pokemon) { return false; } @@ -781,6 +793,7 @@ export class DestinyBondTag extends BattlerTag { return false; } + // Drag the foe down with the user globalScene.phaseManager.queueMessage( i18next.t("battlerTags:destinyBondLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(source), @@ -798,17 +811,13 @@ export class InfatuatedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - if (this.sourceId) { - const pkm = globalScene.getPokemonById(this.sourceId); - - if (pkm) { - return pokemon.isOppositeGender(pkm); - } - console.warn("canAdd: this.sourceId is not a valid pokemon id!", this.sourceId); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfatuatedTag canAdd; id: ${this.sourceId}`); return false; } - console.warn("canAdd: this.sourceId is undefined"); - return false; + + return pokemon.isOppositeGender(source); } onAdd(pokemon: Pokemon): void { @@ -817,7 +826,7 @@ export class InfatuatedTag extends BattlerTag { globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(this.getSourcePokemon()!), // Tag not added + console warns if no source }), ); } @@ -835,28 +844,36 @@ export class InfatuatedTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); - const phaseManager = globalScene.phaseManager; - - if (ret) { - phaseManager.queueMessage( - i18next.t("battlerTags:infatuatedLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? - }), - ); - phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT); - - if (pokemon.randBattleSeedInt(2)) { - phaseManager.queueMessage( - i18next.t("battlerTags:infatuatedLapseImmobilize", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - ); - (phaseManager.getCurrentPhase() as MovePhase).cancel(); - } + if (!ret) { + return false; } - return ret; + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfatuatedTag lapse; id: ${this.sourceId}`); + return false; + } + + const phaseManager = globalScene.phaseManager; + phaseManager.queueMessage( + i18next.t("battlerTags:infatuatedLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + }), + ); + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT); + + // 50% chance to disrupt the target's action + if (pokemon.randBattleSeedInt(2)) { + phaseManager.queueMessage( + i18next.t("battlerTags:infatuatedLapseImmobilize", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + ); + (phaseManager.getCurrentPhase() as MovePhase).cancel(); + } + + return true; } onRemove(pokemon: Pokemon): void { @@ -899,6 +916,12 @@ export class SeedTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SeedTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); globalScene.phaseManager.queueMessage( @@ -906,47 +929,51 @@ export class SeedTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); - if (ret) { - const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex); - if (source) { - const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); - - if (!cancelled.value) { - globalScene.phaseManager.unshiftNew( - "CommonAnimPhase", - source.getBattlerIndex(), - pokemon.getBattlerIndex(), - CommonAnim.LEECH_SEED, - ); - - const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); - const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false); - globalScene.phaseManager.unshiftNew( - "PokemonHealPhase", - source.getBattlerIndex(), - !reverseDrain ? damage : damage * -1, - !reverseDrain - ? i18next.t("battlerTags:seededLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }) - : i18next.t("battlerTags:seededLapseShed", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - false, - true, - ); - } - } + if (!ret) { + return false; } - return ret; + // Check which opponent to restore HP to + const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex); + if (!source) { + console.warn(`Failed to get source Pokemon for SeedTag lapse; id: ${this.sourceId}`); + return false; + } + + const cancelled = new BooleanHolder(false); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); + + if (cancelled.value) { + return true; + } + + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + source.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.LEECH_SEED, + ); + + // Damage the target and restore our HP (or take damage in the case of liquid ooze) + const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); + const reverseDrain = pokemon.hasAbilityWithAttr("ReverseDrainAbAttr", false); + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + source.getBattlerIndex(), + reverseDrain ? -damage : damage, + i18next.t(reverseDrain ? "battlerTags:seededLapseShed" : "battlerTags:seededLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + false, + true, + ); + return true; } getDescriptor(): string { @@ -1006,7 +1033,7 @@ export class PowderTag extends BattlerTag { globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER); const cancelDamage = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelDamage); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled: cancelDamage }); if (!cancelDamage.value) { pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); } @@ -1056,7 +1083,7 @@ export class NightmareTag extends BattlerTag { phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); @@ -1195,9 +1222,15 @@ export class HelpingHandTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for HelpingHandTag onAdd; id: ${this.sourceId}`); + return; + } + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:helpingHandOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + pokemonNameWithAffix: getPokemonNameWithAffix(source), pokemonName: getPokemonNameWithAffix(pokemon), }), ); @@ -1219,9 +1252,7 @@ export class IngrainTag extends TrappedTag { * @returns boolean True if the tag can be added, false otherwise */ canAdd(pokemon: Pokemon): boolean { - const isTrapped = pokemon.getTag(BattlerTagType.TRAPPED); - - return !isTrapped; + return !pokemon.getTag(BattlerTagType.TRAPPED); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1409,7 +1440,7 @@ export abstract class DamagingTrapTag extends TrappedTag { phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); @@ -1420,15 +1451,22 @@ export abstract class DamagingTrapTag extends TrappedTag { } } +// TODO: Condense all these tags into 1 singular tag with a modified message func export class BindTag extends DamagingTrapTag { constructor(turnCount: number, sourceId: number) { super(BattlerTagType.BIND, CommonAnim.BIND, turnCount, MoveId.BIND, sourceId); } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for BindTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:bindOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(source), moveName: this.getMoveName(), }); } @@ -1440,9 +1478,16 @@ export class WrapTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for WrapTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:wrapOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonName: getPokemonNameWithAffix(source), + moveName: this.getMoveName(), }); } } @@ -1473,8 +1518,14 @@ export class ClampTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ClampTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT ASAP"; + } + return i18next.t("battlerTags:clampOnTrap", { - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), pokemonName: getPokemonNameWithAffix(pokemon), }); } @@ -1523,9 +1574,15 @@ export class ThunderCageTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for ThunderCageTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - PLEASE REPORT ASAP"; + } + return i18next.t("battlerTags:thunderCageOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), }); } } @@ -1536,9 +1593,15 @@ export class InfestationTag extends DamagingTrapTag { } getTrapMessage(pokemon: Pokemon): string { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for InfestationTag getTrapMessage; id: ${this.sourceId}`); + return "ERROR - CHECK CONSOLE AND REPORT"; + } + return i18next.t("battlerTags:infestationOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? + sourcePokemonNameWithAffix: getPokemonNameWithAffix(source), }); } } @@ -1642,7 +1705,7 @@ export class ContactDamageProtectedTag extends ContactProtectedTag { */ override onContact(attacker: Pokemon, user: Pokemon): void { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: user, cancelled }); if (!cancelled.value) { attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT, @@ -2221,14 +2284,19 @@ export class SaltCuredTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SaltCureTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); globalScene.phaseManager.queueMessage( i18next.t("battlerTags:saltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -2243,7 +2311,7 @@ export class SaltCuredTag extends BattlerTag { ); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER); @@ -2281,8 +2349,14 @@ export class CursedTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for CursedTag onAdd; id: ${this.sourceId}`); + return; + } + super.onAdd(pokemon); - this.sourceIndex = globalScene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? + this.sourceIndex = source.getBattlerIndex(); } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -2297,7 +2371,7 @@ export class CursedTag extends BattlerTag { ); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); @@ -2632,7 +2706,7 @@ export class GulpMissileTag extends BattlerTag { } const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", attacker, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: attacker, cancelled }); if (!cancelled.value) { attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT }); @@ -2902,7 +2976,13 @@ export class SubstituteTag extends BattlerTag { /** Sets the Substitute's HP and queues an on-add battle animation that initializes the Substitute's sprite. */ onAdd(pokemon: Pokemon): void { - this.hp = Math.floor(globalScene.getPokemonById(this.sourceId!)!.getMaxHp() / 4); + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SubstituteTag onAdd; id: ${this.sourceId}`); + return; + } + + this.hp = Math.floor(source.getMaxHp() / 4); this.sourceInFocus = false; // Queue battle animation and message @@ -3021,14 +3101,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag { const ret = super.lapse(pokemon, lapseType); if (lapseType === BattlerTagLapseType.CUSTOM) { - const cancelled = new BooleanHolder(false); - applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled); - applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", pokemon, cancelled, false, pokemon); - if (!cancelled.value) { - if (pokemon.mysteryEncounterBattleEffects) { - pokemon.mysteryEncounterBattleEffects(pokemon); - } - } + pokemon.mysteryEncounterBattleEffects?.(pokemon); } return ret; @@ -3182,13 +3255,14 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { */ public override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const source = this.getSourcePokemon(); - if (source) { - if (lapseType === BattlerTagLapseType.PRE_MOVE) { - return super.lapse(pokemon, lapseType) && source.isActive(true); - } - return source.isActive(true); + if (!source) { + console.warn(`Failed to get source Pokemon for ImprisonTag lapse; id: ${this.sourceId}`); + return false; } - return false; + if (lapseType === BattlerTagLapseType.PRE_MOVE) { + return super.lapse(pokemon, lapseType) && source.isActive(true); + } + return source.isActive(true); } /** @@ -3248,12 +3322,20 @@ export class SyrupBombTag extends BattlerTag { * Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count * @param pokemon - The target {@linkcode Pokemon} * @param _lapseType - N/A - * @returns `true` if the `turnCount` is still greater than `0`; `false` if the `turnCount` is `0` or the target or source Pokemon has been removed from the field + * @returns Whether the tag should persist (`turnsRemaining > 0` and source still on field) */ override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - if (this.sourceId && !globalScene.getPokemonById(this.sourceId)?.isActive(true)) { + const source = this.getSourcePokemon(); + if (!source) { + console.warn(`Failed to get source Pokemon for SyrupBombTag lapse; id: ${this.sourceId}`); return false; } + + // Syrup bomb clears immediately if source leaves field/faints + if (!source.isActive(true)) { + return false; + } + // Custom message in lieu of an animation in mainline globalScene.phaseManager.queueMessage( i18next.t("battlerTags:syrupBombLapse", { @@ -3270,7 +3352,7 @@ export class SyrupBombTag extends BattlerTag { false, true, ); - return --this.turnCount > 0; + return super.lapse(pokemon, _lapseType); } } diff --git a/src/data/berry.ts b/src/data/berry.ts index 7d1e62362a8..be6e5c28f84 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -35,28 +35,28 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { case BerryType.APICOT: case BerryType.SALAC: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); + const hpRatioReq = new NumberHolder(0.25); // Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth const stat: BattleStat = berryType - BerryType.ENIGMA; - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); - return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6; + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); + return pokemon.getHpRatio() < hpRatioReq.value && pokemon.getStatStage(stat) < 6; }; case BerryType.LANSAT: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return pokemon.getHpRatio() < 0.25; }; case BerryType.LEPPA: return (pokemon: Pokemon) => { - const threshold = new NumberHolder(0.25); - applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold); + const hpRatioReq = new NumberHolder(0.25); + applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq }); return !!pokemon.getMoveset().find(m => !m.getPpRatio()); }; } @@ -72,7 +72,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.ENIGMA: { const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4)); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: hpHealed }); globalScene.phaseManager.unshiftNew( "PokemonHealPhase", consumer.getBattlerIndex(), @@ -105,7 +105,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { // Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc. const stat: BattleStat = berryType - BerryType.ENIGMA; const statStages = new NumberHolder(1); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: statStages }); globalScene.phaseManager.unshiftNew( "StatStageChangePhase", consumer.getBattlerIndex(), @@ -126,7 +126,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { { const randStat = randSeedInt(Stat.SPD, Stat.ATK); const stages = new NumberHolder(2); - applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages); + applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: stages }); globalScene.phaseManager.unshiftNew( "StatStageChangePhase", consumer.getBattlerIndex(), diff --git a/src/data/egg.ts b/src/data/egg.ts index a6e2e04a5fe..1ef08a4f1da 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -175,7 +175,7 @@ export class Egg { this._sourceType = eggOptions?.sourceType ?? undefined; this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); + this._timestamp = eggOptions?.timestamp ?? Date.now(); // First roll shiny and variant so we can filter if species with an variant exist this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); @@ -255,7 +255,7 @@ export class Egg { // Sets the hidden ability if a hidden ability exists and // the override is set or the egg hits the chance - let abilityIndex: number | undefined = undefined; + let abilityIndex: number | undefined; const sameSpeciesEggHACheck = this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE); const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE); @@ -524,7 +524,7 @@ export class Egg { /** * Rolls whether the egg is shiny or not. * @returns `true` if the egg is shiny - **/ + */ private rollShiny(): boolean { let shinyChance = GACHA_DEFAULT_SHINY_RATE; switch (this._sourceType) { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f94c59bb463..07493a76a6c 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -33,11 +33,7 @@ import type { ArenaTrapTag } from "../arena-tag"; import { WeakenMoveTypeTag } from "../arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import { - applyAbAttrs, - applyPostAttackAbAttrs, - applyPostItemLostAbAttrs, - applyPreAttackAbAttrs, - applyPreDefendAbAttrs + applyAbAttrs } from "../abilities/apply-ab-attrs"; import { allAbilities, allMoves } from "../data-lists"; import { @@ -89,9 +85,10 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves, invalidSketchMoves } from "./invalid-moves"; import { isVirtual, MoveUseMode } from "#enums/move-use-mode"; -import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types"; +import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types"; import { applyMoveAttrs } from "./apply-attrs"; import { frenzyMissFunc, getMoveTargets } from "./move-utils"; +import { AbAttrBaseParams, AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "../abilities/ability"; /** * A function used to conditionally determine execution of a given {@linkcode MoveAttr}. @@ -347,7 +344,7 @@ export default abstract class Move implements Localizable { const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated - applyAbAttrs("InfiltratorAbAttr", user, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", {pokemon: user, bypassed}); return !bypassed.value && !this.hasFlag(MoveFlags.SOUND_BASED) @@ -645,7 +642,7 @@ export default abstract class Move implements Localizable { case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr("MoveAbilityBypassAbAttr")) { const abilityEffectsIgnored = new BooleanHolder(false); - applyAbAttrs("MoveAbilityBypassAbAttr", user, abilityEffectsIgnored, false, this); + applyAbAttrs("MoveAbilityBypassAbAttr", {pokemon: user, cancelled: abilityEffectsIgnored, move: this}); if (abilityEffectsIgnored.value) { return true; } @@ -683,13 +680,7 @@ export default abstract class Move implements Localizable { * @returns boolean: false if any of the apply()'s return false, else true */ applyConditions(user: Pokemon, target: Pokemon, move: Move): boolean { - for (const condition of this.conditions) { - if (!condition.apply(user, target, move)) { - return false; - } - } - - return true; + return this.conditions.every(cond => cond.apply(user, target, move)); } /** @@ -762,7 +753,7 @@ export default abstract class Move implements Localizable { const moveAccuracy = new NumberHolder(this.accuracy); applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy); - applyPreDefendAbAttrs("WonderSkinAbAttr", target, user, this, { value: false }, simulated, moveAccuracy); + applyAbAttrs("WonderSkinAbAttr", {pokemon: target, opponent: user, move: this, simulated, accuracy: moveAccuracy}); if (moveAccuracy.value === -1) { return moveAccuracy.value; @@ -805,17 +796,25 @@ export default abstract class Move implements Localizable { const typeChangeMovePowerMultiplier = new NumberHolder(1); const typeChangeHolder = new NumberHolder(this.type); - applyPreAttackAbAttrs("MoveTypeChangeAbAttr", source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier); + applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier}); const sourceTeraType = source.getTeraType(); if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { power.value = 60; } - applyPreAttackAbAttrs("VariableMovePowerAbAttr", source, target, this, simulated, power); + const abAttrParams: PreAttackModifyPowerAbAttrParams = { + pokemon: source, + opponent: target, + simulated, + power, + move: this, + } + + applyAbAttrs("VariableMovePowerAbAttr", abAttrParams); const ally = source.getAlly(); if (!isNullOrUndefined(ally)) { - applyPreAttackAbAttrs("AllyMoveCategoryPowerBoostAbAttr", ally, target, this, simulated, power); + applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally}); } const fieldAuras = new Set( @@ -827,11 +826,12 @@ export default abstract class Move implements Localizable { .flat(), ); for (const aura of fieldAuras) { - aura.applyPreAttack(source, null, simulated, target, this, [ power ]); + // TODO: Refactor the fieldAura attribute so that its apply method is not directly called + aura.apply({pokemon: source, simulated, opponent: target, move: this, power}); } const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); - alliedField.forEach(p => applyPreAttackAbAttrs("UserFieldMoveTypePowerBoostAbAttr", p, target, this, simulated, power)); + alliedField.forEach(p => applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power})); power.value *= typeChangeMovePowerMultiplier.value; @@ -858,7 +858,7 @@ export default abstract class Move implements Localizable { const priority = new NumberHolder(this.priority); applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority); - applyAbAttrs("ChangeMovePriorityAbAttr", user, null, simulated, this, priority); + applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority}); return priority.value; } @@ -1310,7 +1310,7 @@ export class MoveEffectAttr extends MoveAttr { getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number { const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance); - applyAbAttrs("MoveEffectChanceMultiplierAbAttr", user, null, !showAbility, moveChance, move); + applyAbAttrs("MoveEffectChanceMultiplierAbAttr", {pokemon: user, simulated: !showAbility, chance: moveChance, move}); if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) { const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -1318,7 +1318,7 @@ export class MoveEffectAttr extends MoveAttr { } if (!selfEffect) { - applyPreDefendAbAttrs("IgnoreMoveEffectsAbAttr", target, user, null, null, !showAbility, moveChance); + applyAbAttrs("IgnoreMoveEffectsAbAttr", {pokemon: target, move, simulated: !showAbility, chance: moveChance}); } return moveChance.value; } @@ -1709,8 +1709,9 @@ export class RecoilAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); if (!this.unblockable) { - applyAbAttrs("BlockRecoilDamageAttr", user, cancelled); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + const abAttrParams: AbAttrParamsWithCancel = {pokemon: user, cancelled}; + applyAbAttrs("BlockRecoilDamageAttr", abAttrParams); + applyAbAttrs("BlockNonDirectDamageAbAttr", abAttrParams); } if (cancelled.value) { @@ -1843,7 +1844,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); // Check to see if the Pokemon has an ability that blocks non-direct damage - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); if (!cancelled.value) { user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message @@ -2042,7 +2043,7 @@ export class FlameBurstAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); if (!isNullOrUndefined(targetAlly)) { - applyAbAttrs("BlockNonDirectDamageAbAttr", targetAlly, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: targetAlly, cancelled}); } if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) { @@ -2414,7 +2415,7 @@ export class MultiHitAttr extends MoveAttr { { const rand = user.randBattleSeedInt(20); const hitValue = new NumberHolder(rand); - applyAbAttrs("MaxMultiHitAbAttr", user, null, false, hitValue); + applyAbAttrs("MaxMultiHitAbAttr", {pokemon: user, hits: hitValue}); if (hitValue.value >= 13) { return 2; } else if (hitValue.value >= 6) { @@ -2522,7 +2523,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, quiet)) { - applyPostAttackAbAttrs("ConfusionOnStatusEffectAbAttr", user, target, move, null, false, this.effect); + applyAbAttrs("ConfusionOnStatusEffectAbAttr", {pokemon: user, opponent: target, move, effect: this.effect}); return true; } } @@ -2574,7 +2575,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { const statusToApply: StatusEffect | undefined = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined); - if (target.status) { + if (target.status || !statusToApply) { return false; } else { const canSetStatus = target.canSetStatus(statusToApply, true, false, user); @@ -2590,7 +2591,8 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return !target.status && target.canSetStatus(user.status?.effect, true, false, user) ? -10 : 0; + const statusToApply = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined); + return !target.status && statusToApply && target.canSetStatus(statusToApply, true, false, user) ? -10 : 0; } } @@ -2678,7 +2680,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { // Check for abilities that block item theft // TODO: This should not trigger if the target would faint beforehand const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", target, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); if (cancelled.value) { return false; @@ -2795,8 +2797,8 @@ export class EatBerryAttr extends MoveEffectAttr { protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) { // consumer eats berry, owner triggers unburden and similar effects getBerryEffectFunc(this.chosenBerry.berryType)(consumer); - applyPostItemLostAbAttrs("PostItemLostAbAttr", berryOwner, false); - applyAbAttrs("HealFromBerryUseAbAttr", consumer, new BooleanHolder(false)); + applyAbAttrs("PostItemLostAbAttr", {pokemon: berryOwner}); + applyAbAttrs("HealFromBerryUseAbAttr", {pokemon: consumer}); consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest); } } @@ -2821,7 +2823,7 @@ export class StealEatBerryAttr extends EatBerryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // check for abilities that block item theft const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", target, cancelled); + applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); if (cancelled.value === true) { return false; } @@ -2835,7 +2837,7 @@ export class StealEatBerryAttr extends EatBerryAttr { // pick a random berry and eat it this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)]; - applyPostItemLostAbAttrs("PostItemLostAbAttr", target, false); + applyAbAttrs("PostItemLostAbAttr", {pokemon: target}); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name }); globalScene.phaseManager.queueMessage(message); this.reduceBerryModifier(target); @@ -3026,7 +3028,7 @@ export class OneHitKOAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockOneHitKOAbAttr", target, cancelled); + applyAbAttrs("BlockOneHitKOAbAttr", {pokemon: target, cancelled}); return !cancelled.value && user.level >= target.level; }; } @@ -5438,7 +5440,7 @@ export class NoEffectAttr extends MoveAttr { const crashDamageFunc = (user: Pokemon, move: Move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); if (cancelled.value) { return false; } @@ -6437,9 +6439,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined { - const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); - if (blockedByAbility.value) { + const cancelled = new BooleanHolder(false); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled}); + if (cancelled.value) { return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }); } } @@ -6478,7 +6480,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled: blockedByAbility}); if (blockedByAbility.value) { return false; } @@ -6887,12 +6889,12 @@ export class RandomMovesetMoveAttr extends CallMoveAttr { // includeParty will be true for Assist, false for Sleep Talk let allies: Pokemon[]; if (this.includeParty) { - allies = user.isPlayer() ? globalScene.getPlayerParty().filter(p => p !== user) : globalScene.getEnemyParty().filter(p => p !== user); + allies = (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter(p => p !== user); } else { allies = [ user ]; } - const partyMoveset = allies.map(p => p.moveset).flat(); - const moves = partyMoveset.filter(m => !this.invalidMoves.has(m!.moveId) && !m!.getMove().name.endsWith(" (N)")); + const partyMoveset = allies.flatMap(p => p.moveset); + const moves = partyMoveset.filter(m => !this.invalidMoves.has(m.moveId) && !m.getMove().name.endsWith(" (N)")); if (moves.length === 0) { return false; } @@ -7987,7 +7989,7 @@ const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScen const failIfDampCondition: MoveConditionFunc = (user, target, move) => { const cancelled = new BooleanHolder(false); - globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled)); + globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled})); // Queue a message if an ability prevented usage of the move if (cancelled.value) { globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name })); diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts index daad199fbbd..ab774c9fff9 100644 --- a/src/data/moves/pokemon-move.ts +++ b/src/data/moves/pokemon-move.ts @@ -16,7 +16,7 @@ import type Move from "./move"; * @see {@linkcode getMovePp} - returns amount of PP a move currently has. * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. * @see {@linkcode getName} - returns name of {@linkcode Move}. - **/ + */ export class PokemonMove { public moveId: MoveId; public ppUsed: number; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 9bdaa603540..818318bb499 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -135,7 +135,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder ); clownConfig.setPartyTemplates(clownPartyTemplate); clownConfig.setDoubleOnly(); - // @ts-ignore + // @ts-expect-error clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists // Generate random ability for Blacephalon from pool diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index c68e395b379..cf24d1dd7e0 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -328,7 +328,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder .withOptionPhase(async () => { // Show the Oricorio a dance, and recruit it const encounter = globalScene.currentBattle.mysteryEncounter!; - const oricorio = encounter.misc.oricorioData.toPokemon(); + const oricorio = encounter.misc.oricorioData.toPokemon() as EnemyPokemon; oricorio.passive = true; // Ensure the Oricorio's moveset gains the Dance move the player used diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 010358ea3b2..ae3d905cf91 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -92,7 +92,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter const brutalConfig = trainerConfigs[brutalTrainerType].clone(); brutalConfig.title = trainerConfigs[brutalTrainerType].title; brutalConfig.setPartyTemplates(e4Template); - // @ts-ignore + // @ts-expect-error brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func female = false; if (brutalConfig.hasGenders) { diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index eba8a6ba00e..6d28a710953 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -24,7 +24,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { BerryType } from "#enums/berry-type"; import { Stat } from "#enums/stat"; import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import i18next from "i18next"; @@ -221,7 +221,7 @@ function endTrainerBattleAndShowDialogue(): Promise { // Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects pokemon.resetBattleAndWaveData(); - applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon); + applyAbAttrs("PostBattleInitAbAttr", { pokemon }); } globalScene.phaseManager.unshiftNew("ShowTrainerPhase"); diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index bb74f11ce60..f698f636cac 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1226,7 +1226,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { ); for (const value of meanEncountersPerRunPerBiomeSorted) { - stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + ", avg MEs ${value[1]},\n"; + stats += value[0] + "avg valid floors " + meanMEFloorsPerRunPerBiome.get(value[0]) + `, avg MEs ${value[1]},\n`; } console.log(stats); diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 39e8fbd18e6..d2a48495195 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -96,8 +96,8 @@ export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): Po } export function getFusedSpeciesName(speciesAName: string, speciesBName: string): string { - const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\']+)(.*?)$/i; - const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-\'])(.*?)$/i; + const fragAPattern = /([a-z]{2}.*?[aeiou(?:y$)\-']+)(.*?)$/i; + const fragBPattern = /([a-z]{2}.*?[aeiou(?:y$)\-'])(.*?)$/i; const [speciesAPrefixMatch, speciesBPrefixMatch] = [speciesAName, speciesBName].map(n => /^(?:[^ ]+) /.exec(n)); const [speciesAPrefix, speciesBPrefix] = [speciesAPrefixMatch, speciesBPrefixMatch].map(m => (m ? m[0] : "")); @@ -134,7 +134,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string): if (fragBMatch) { const lastCharA = fragA.slice(fragA.length - 1); const prevCharB = fragBMatch[1].slice(fragBMatch.length - 1); - fragB = (/[\-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB; + fragB = (/[-']/.test(prevCharB) ? prevCharB : "") + fragBMatch[2] || prevCharB; if (lastCharA === fragB[0]) { if (/[aiu]/.test(lastCharA)) { fragB = fragB.slice(1); @@ -379,7 +379,7 @@ export abstract class PokemonSpeciesForm { } getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number, back?: boolean): string { - const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/\_{2}/g, "/"); + const spriteId = this.getSpriteId(female, formIndex, shiny, variant, back).replace(/_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } @@ -478,8 +478,8 @@ export abstract class PokemonSpeciesForm { case SpeciesId.DUDUNSPARCE: break; case SpeciesId.ZACIAN: + // biome-ignore lint/suspicious/noFallthroughSwitchClause: Intentionally falls through case SpeciesId.ZAMAZENTA: - // biome-ignore lint/suspicious/noFallthroughSwitchClause: Falls through if (formSpriteKey.startsWith("behemoth")) { formSpriteKey = "crowned"; } @@ -569,7 +569,7 @@ export abstract class PokemonSpeciesForm { const rootSpeciesId = this.getRootSpeciesId(); for (const moveId of moveset) { if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) { - const eggMoveIndex = speciesEggMoves[rootSpeciesId].findIndex(m => m === moveId); + const eggMoveIndex = speciesEggMoves[rootSpeciesId].indexOf(moveId); if (eggMoveIndex > -1 && eggMoves & (1 << eggMoveIndex)) { continue; } diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 6786aa00ef7..7c8b5b29fcd 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -290,7 +290,7 @@ export class TrainerConfig { * @param {string} [nameFemale] The name of the female trainer. If 'Ivy', a localized name will be assigned. * @param {TrainerType | string} [femaleEncounterBgm] The encounter BGM for the female trainer, which can be a TrainerType or a string. * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { // If the female name is 'Ivy' (the rival), assign a localized name. if (nameFemale === "Ivy") { @@ -335,7 +335,7 @@ export class TrainerConfig { if (doubleEncounterBgm) { this.doubleEncounterBgm = typeof doubleEncounterBgm === "number" - ? TrainerType[doubleEncounterBgm].toString().replace(/\_/g, " ").toLowerCase() + ? TrainerType[doubleEncounterBgm].toString().replace(/_/g, " ").toLowerCase() : doubleEncounterBgm; } return this; @@ -540,7 +540,7 @@ export class TrainerConfig { * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the evil team leader. * @param specialtyType The specialty Type of the admin, if they have one * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForEvilTeamAdmin( title: string, poolName: EvilTeam, @@ -581,7 +581,7 @@ export class TrainerConfig { * Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter. * @param _isMale Whether the stat trainer is Male or Female (for localization of the title). * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ initForStatTrainer(_isMale = false): TrainerConfig { if (!getIsInitialized()) { initI18n(); @@ -608,7 +608,7 @@ export class TrainerConfig { * @param {PokemonType} specialtyType The specialty type for the evil team Leader. * @param boolean Whether or not this is the rematch fight * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForEvilTeamLeader( title: string, signatureSpecies: (SpeciesId | SpeciesId[])[], @@ -651,7 +651,7 @@ export class TrainerConfig { * @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false. * @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size. * @returns {TrainerConfig} The updated TrainerConfig instance. - * **/ + */ initForGymLeader( signatureSpecies: (SpeciesId | SpeciesId[])[], isMale: boolean, @@ -709,7 +709,7 @@ export class TrainerConfig { * @param specialtyType - The specialty type for the Elite Four member. * @param teraSlot - Optional, sets the party member in this slot to Terastallize. * @returns The updated TrainerConfig instance. - **/ + */ initForEliteFour( signatureSpecies: (SpeciesId | SpeciesId[])[], isMale: boolean, @@ -765,7 +765,7 @@ export class TrainerConfig { * @param {SpeciesId | SpeciesId[]} signatureSpecies The signature species for the Champion. * @param isMale Whether the Champion is Male or Female (for localization of the title). * @returns {TrainerConfig} The updated TrainerConfig instance. - **/ + */ initForChampion(isMale: boolean): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { @@ -815,7 +815,7 @@ export class TrainerConfig { * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. * @returns {string} - The title of the trainer. - **/ + */ getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { const ret = this.name; diff --git a/src/field/arena.ts b/src/field/arena.ts index 8d7e5037852..6893678d4a8 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -20,11 +20,7 @@ import { ArenaTrapTag, getArenaTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import type { BattlerIndex } from "#enums/battler-index"; import { Terrain, TerrainType } from "#app/data/terrain"; -import { - applyAbAttrs, - applyPostTerrainChangeAbAttrs, - applyPostWeatherChangeAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import type Pokemon from "#app/field/pokemon"; import Overrides from "#app/overrides"; import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; @@ -372,7 +368,7 @@ export class Arena { pokemon.findAndRemoveTags( t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather), ); - applyPostWeatherChangeAbAttrs("PostWeatherChangeAbAttr", pokemon, weather); + applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather }); }); return true; @@ -461,8 +457,8 @@ export class Arena { pokemon.findAndRemoveTags( t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain), ); - applyPostTerrainChangeAbAttrs("PostTerrainChangeAbAttr", pokemon, terrain); - applyAbAttrs("TerrainEventTypeChangeAbAttr", pokemon, null, false); + applyAbAttrs("PostTerrainChangeAbAttr", { pokemon, terrain }); + applyAbAttrs("TerrainEventTypeChangeAbAttr", { pokemon }); }); return true; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e9cc4f70d70..eee6c309859 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -108,23 +108,8 @@ import { WeatherType } from "#enums/weather-type"; import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import type { SuppressAbilitiesTag } from "#app/data/arena-tag"; -import type { Ability } from "#app/data/abilities/ability"; -import { - applyAbAttrs, - applyStatMultiplierAbAttrs, - applyPreApplyBattlerTagAbAttrs, - applyPreAttackAbAttrs, - applyPreDefendAbAttrs, - applyPreSetStatusAbAttrs, - applyFieldStatMultiplierAbAttrs, - applyCheckTrappedAbAttrs, - applyPostDamageAbAttrs, - applyPostItemLostAbAttrs, - applyOnGainAbAttrs, - applyPreLeaveFieldAbAttrs, - applyOnLoseAbAttrs, - applyAllyStatMultiplierAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import type { Ability, PreAttackModifyDamageAbAttrParams } from "#app/data/abilities/ability"; +import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { allAbilities } from "#app/data/data-lists"; import type PokemonData from "#app/system/pokemon-data"; import { BattlerIndex } from "#enums/battler-index"; @@ -189,7 +174,7 @@ import { HitResult } from "#enums/hit-result"; import { AiType } from "#enums/ai-type"; import type { MoveResult } from "#enums/move-result"; import { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types"; +import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#app/@types/ability-types"; /** Base typeclass for damage parameter methods, used for DRY */ type damageParams = { @@ -898,12 +883,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteAtlasPath(ignoreOverride?: boolean): string { - const spriteId = this.getSpriteId(ignoreOverride).replace(/\_{2}/g, "/"); + const spriteId = this.getSpriteId(ignoreOverride).replace(/_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } getBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { - const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); + const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } @@ -977,7 +962,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { - return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); + return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/_{2}/g, "/"); } getIconAtlasKey(ignoreOverride = false, useIllusion = true): string { @@ -1364,7 +1349,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("HighCritAttr", source, this, move, critStage); globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage); globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage); - applyAbAttrs("BonusCritAbAttr", source, null, false, critStage); + applyAbAttrs("BonusCritAbAttr", { pokemon: source, critStage }); const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { // Dragon cheer only gives +1 crit stage to non-dragon types @@ -1415,46 +1400,52 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { simulated = true, ignoreHeldItems = false, ): number { - const statValue = new NumberHolder(this.getStat(stat, false)); + const statVal = new NumberHolder(this.getStat(stat, false)); if (!ignoreHeldItems) { - globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue); + globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statVal); } // The Ruin abilities here are never ignored, but they reveal themselves on summon anyway const fieldApplied = new BooleanHolder(false); for (const pokemon of globalScene.getField(true)) { - applyFieldStatMultiplierAbAttrs( - "FieldMultiplyStatAbAttr", + applyAbAttrs("FieldMultiplyStatAbAttr", { pokemon, stat, - statValue, - this, - fieldApplied, + statVal, + target: this, + hasApplied: fieldApplied, simulated, - ); + }); if (fieldApplied.value) { break; } } if (!ignoreAbility) { - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, stat, statValue, simulated); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: this, + stat, + statVal, + simulated, + // TODO: maybe just don't call this if the move is none? + move: move ?? allMoves[MoveId.NONE], + }); } const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, stat, - statValue, + statVal, simulated, - this, - move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility, - ); + // TODO: maybe just don't call this if the move is none? + move: move ?? allMoves[MoveId.NONE], + ignoreAbility: move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility, + }); } let ret = - statValue.value * + statVal.value * this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems); switch (stat) { @@ -2045,20 +2036,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ability New Ability */ public setTempAbility(ability: Ability, passive = false): void { - applyOnLoseAbAttrs(this, passive); + applyOnLoseAbAttrs({ pokemon: this, passive }); if (passive) { this.summonData.passiveAbility = ability.id; } else { this.summonData.ability = ability.id; } - applyOnGainAbAttrs(this, passive); + applyOnGainAbAttrs({ pokemon: this, passive }); } /** * Suppresses an ability and calls its onlose attributes */ public suppressAbility() { - [true, false].forEach(passive => applyOnLoseAbAttrs(this, passive)); + [true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: this, passive })); this.summonData.abilitySuppressed = true; } @@ -2194,7 +2185,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const weight = new NumberHolder(this.species.weight - weightRemoved); // This will trigger the ability overlay so only call this function when necessary - applyAbAttrs("WeightMultiplierAbAttr", this, null, false, weight); + applyAbAttrs("WeightMultiplierAbAttr", { pokemon: this, weight }); return Math.max(minWeight, weight.value); } @@ -2256,7 +2247,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const trappedByAbility = new BooleanHolder(false); + /** Holds whether the pokemon is trapped due to an ability */ + const trapped = new BooleanHolder(false); /** * Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective * Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger @@ -2265,14 +2257,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false); for (const opponent of opposingField) { - applyCheckTrappedAbAttrs("CheckTrappedAbAttr", opponent, trappedByAbility, this, trappedAbMessages, simulated); + applyAbAttrs("CheckTrappedAbAttr", { pokemon: opponent, trapped, opponent: this, simulated }, trappedAbMessages); } const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; return ( - trappedByAbility.value || - !!this.getTag(TrappedTag) || - !!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side) + trapped.value || !!this.getTag(TrappedTag) || !!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side) ); } @@ -2287,7 +2277,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveTypeHolder = new NumberHolder(move.type); applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder); - applyPreAttackAbAttrs("MoveTypeChangeAbAttr", this, null, move, simulated, moveTypeHolder); + + const power = new NumberHolder(move.power); + applyAbAttrs("MoveTypeChangeAbAttr", { + pokemon: this, + move, + simulated, + moveType: moveTypeHolder, + power, + opponent: this, + }); // If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type, // then bypass the check for ion deluge and electrify @@ -2351,17 +2350,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const cancelledHolder = cancelled ?? new BooleanHolder(false); + // TypeMultiplierAbAttrParams is shared amongst the type of AbAttrs we will be invoking + const commonAbAttrParams: TypeMultiplierAbAttrParams = { + pokemon: this, + opponent: source, + move, + cancelled: cancelledHolder, + simulated, + typeMultiplier, + }; if (!ignoreAbility) { - applyPreDefendAbAttrs("TypeImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("TypeImmunityAbAttr", commonAbAttrParams); if (!cancelledHolder.value) { - applyPreDefendAbAttrs("MoveImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("MoveImmunityAbAttr", commonAbAttrParams); } if (!cancelledHolder.value) { const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); defendingSidePlayField.forEach(p => - applyPreDefendAbAttrs("FieldPriorityMoveImmunityAbAttr", p, source, move, cancelledHolder), + applyAbAttrs("FieldPriorityMoveImmunityAbAttr", { + pokemon: p, + opponent: source, + move, + cancelled: cancelledHolder, + }), ); } } @@ -2376,7 +2389,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Apply Tera Shell's effect to attacks after all immunities are accounted for if (!ignoreAbility && move.category !== MoveCategory.STATUS) { - applyPreDefendAbAttrs("FullHpResistTypeAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier); + applyAbAttrs("FullHpResistTypeAbAttr", commonAbAttrParams); } if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) { @@ -2420,16 +2433,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } let multiplier = types - .map(defType => { - const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType)); + .map(defenderType => { + const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defenderType)); applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier); if (move) { - applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defType); + applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defenderType); } if (source) { const ignoreImmunity = new BooleanHolder(false); if (source.isActive(true) && source.hasAbilityWithAttr("IgnoreTypeImmunityAbAttr")) { - applyAbAttrs("IgnoreTypeImmunityAbAttr", source, ignoreImmunity, simulated, moveType, defType); + applyAbAttrs("IgnoreTypeImmunityAbAttr", { + pokemon: source, + cancelled: ignoreImmunity, + simulated, + moveType, + defenderType, + }); } if (ignoreImmunity.value) { if (multiplier.value === 0) { @@ -2438,7 +2457,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; - if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { + if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType))) { if (multiplier.value === 0) { return 1; } @@ -2498,14 +2517,39 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { defScore *= 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25); } + atkScore *= 1.25; //give more value for the pokemon's typing + const moveset = this.moveset; + let moveAtkScoreLength = 0; + for (const move of moveset) { + if (move.getMove().category === MoveCategory.SPECIAL || move.getMove().category === MoveCategory.PHYSICAL) { + atkScore += opponent.getAttackTypeEffectiveness(move.getMove().type, this, false, true, undefined, true); + moveAtkScoreLength++; + } + } + atkScore = atkScore / (moveAtkScoreLength + 1); //calculate the median for the attack score /** * Based on this Pokemon's HP ratio compared to that of the opponent. * This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent; * however, the final ratio cannot be higher than 1. */ - let hpDiffRatio = this.getHpRatio() + (1 - opponent.getHpRatio()); - if (outspeed) { - hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1); + const hpRatio = this.getHpRatio(); + const oppHpRatio = opponent.getHpRatio(); + const isDying = hpRatio <= 0.2; + let hpDiffRatio = hpRatio + (1 - oppHpRatio); + if (isDying && this.isActive(true)) { + //It might be a sacrifice candidate if hp under 20% + const badMatchup = atkScore < 1.5 && defScore < 1.5; + if (!outspeed && badMatchup) { + //It might not be a worthy sacrifice if it doesn't outspeed or doesn't do enough damage + hpDiffRatio *= 0.85; + } else { + hpDiffRatio = Math.min(1 - hpRatio + (outspeed ? 0.2 : 0.1), 1); + } + } else if (outspeed) { + hpDiffRatio = Math.min(hpDiffRatio * 1.25, 1); + } else if (hpRatio > 0.2 && hpRatio <= 0.4) { + //Might be considered to be switched because it's not in low enough health + hpDiffRatio = Math.min(hpDiffRatio * 0.5, 1); } return (atkScore + defScore) * hpDiffRatio; } @@ -2880,7 +2924,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); }; - let fusionOverride: PokemonSpecies | undefined = undefined; + let fusionOverride: PokemonSpecies | undefined; if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); @@ -3358,7 +3402,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } if (!ignoreOppAbility) { - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", opponent, null, simulated, stat, ignoreStatStage); + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { + pokemon: opponent, + ignored: ignoreStatStage, + stat, + simulated, + }); } if (move) { applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage); @@ -3397,8 +3446,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const ignoreAccStatStage = new BooleanHolder(false); const ignoreEvaStatStage = new BooleanHolder(false); - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", target, null, false, Stat.ACC, ignoreAccStatStage); - applyAbAttrs("IgnoreOpponentStatStagesAbAttr", this, null, false, Stat.EVA, ignoreEvaStatStage); + // TODO: consider refactoring this method to accept `simulated` and then pass simulated to these applyAbAttrs + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: target, stat: Stat.ACC, ignored: ignoreAccStatStage }); + applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: this, stat: Stat.EVA, ignored: ignoreEvaStatStage }); applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage); globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage); @@ -3418,33 +3468,40 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6)); } - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, Stat.ACC, accuracyMultiplier, false, sourceMove); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: this, + stat: Stat.ACC, + statVal: accuracyMultiplier, + move: sourceMove, + }); const evasionMultiplier = new NumberHolder(1); - applyStatMultiplierAbAttrs("StatMultiplierAbAttr", target, Stat.EVA, evasionMultiplier); + applyAbAttrs("StatMultiplierAbAttr", { + pokemon: target, + stat: Stat.EVA, + statVal: evasionMultiplier, + move: sourceMove, + }); const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { const ignore = this.hasAbilityWithAttr("MoveAbilityBypassAbAttr") || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES); - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, - Stat.ACC, - accuracyMultiplier, - false, - this, - ignore, - ); - applyAllyStatMultiplierAbAttrs( - "AllyStatMultiplierAbAttr", - ally, - Stat.EVA, - evasionMultiplier, - false, - this, - ignore, - ); + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, + stat: Stat.ACC, + statVal: accuracyMultiplier, + ignoreAbility: ignore, + move: sourceMove, + }); + + applyAbAttrs("AllyStatMultiplierAbAttr", { + pokemon: ally, + stat: Stat.EVA, + statVal: evasionMultiplier, + ignoreAbility: ignore, + move: sourceMove, + }); } return accuracyMultiplier.value / evasionMultiplier.value; @@ -3559,7 +3616,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier); if (!ignoreSourceAbility) { - applyAbAttrs("StabBoostAbAttr", source, null, simulated, stabMultiplier); + applyAbAttrs("StabBoostAbAttr", { pokemon: source, simulated, multiplier: stabMultiplier }); } if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) { @@ -3706,16 +3763,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { null, multiStrikeEnhancementMultiplier, ); + if (!ignoreSourceAbility) { - applyPreAttackAbAttrs( - "AddSecondStrikeAbAttr", - source, - this, + applyAbAttrs("AddSecondStrikeAbAttr", { + pokemon: source, move, simulated, - null, - multiStrikeEnhancementMultiplier, - ); + multiplier: multiStrikeEnhancementMultiplier, + }); } /** Doubles damage if this Pokemon's last move was Glaive Rush */ @@ -3726,7 +3781,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** The damage multiplier when the given move critically hits */ const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1); - applyAbAttrs("MultCritAbAttr", source, null, simulated, criticalMultiplier); + applyAbAttrs("MultCritAbAttr", { pokemon: source, simulated, critMult: criticalMultiplier }); /** * A multiplier for random damage spread in the range [0.85, 1] @@ -3747,7 +3802,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { const burnDamageReductionCancelled = new BooleanHolder(false); if (!ignoreSourceAbility) { - applyAbAttrs("BypassBurnDamageReductionAbAttr", source, burnDamageReductionCancelled, simulated); + applyAbAttrs("BypassBurnDamageReductionAbAttr", { + pokemon: source, + cancelled: burnDamageReductionCancelled, + simulated, + }); } if (!burnDamageReductionCancelled.value) { burnMultiplier = 0.5; @@ -3811,7 +3870,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Doubles damage if the attacker has Tinted Lens and is using a resisted move */ if (!ignoreSourceAbility) { - applyPreAttackAbAttrs("DamageBoostAbAttr", source, this, move, simulated, damage); + applyAbAttrs("DamageBoostAbAttr", { + pokemon: source, + opponent: this, + move, + simulated, + damage, + }); } /** Apply the enemy's Damage and Resistance tokens */ @@ -3822,14 +3887,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.applyModifiers(EnemyDamageReducerModifier, false, damage); } + const abAttrParams: PreAttackModifyDamageAbAttrParams = { + pokemon: this, + opponent: source, + move, + simulated, + damage, + }; /** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */ if (!ignoreAbility) { - applyPreDefendAbAttrs("ReceivedMoveDamageMultiplierAbAttr", this, source, move, cancelled, simulated, damage); + applyAbAttrs("ReceivedMoveDamageMultiplierAbAttr", abAttrParams); const ally = this.getAlly(); /** Additionally apply friend guard damage reduction if ally has it. */ if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) { - applyPreDefendAbAttrs("AlliedFieldDamageReductionAbAttr", ally, source, move, cancelled, simulated, damage); + applyAbAttrs("AlliedFieldDamageReductionAbAttr", { + ...abAttrParams, + // Same parameters as before, except we are applying the ally's ability + pokemon: ally, + }); } } @@ -3837,7 +3913,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage); if (this.isFullHp() && !ignoreAbility) { - applyPreDefendAbAttrs("PreDefendFullHpEndureAbAttr", this, source, move, cancelled, false, damage); + applyAbAttrs("PreDefendFullHpEndureAbAttr", abAttrParams); } // debug message for when damage is applied (i.e. not simulated) @@ -3875,7 +3951,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const alwaysCrit = new BooleanHolder(false); applyMoveAttrs("CritOnlyAttr", source, this, move, alwaysCrit); - applyAbAttrs("ConditionalCritAbAttr", source, null, false, alwaysCrit, this, move); + applyAbAttrs("ConditionalCritAbAttr", { pokemon: source, isCritical: alwaysCrit, target: this, move }); const alwaysCritTag = !!source.getTag(BattlerTagType.ALWAYS_CRIT); const critChance = [24, 8, 2, 1][Phaser.Math.Clamp(this.getCritStage(source, move), 0, 3)]; @@ -3886,7 +3962,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // apply crit block effects from lucky chant & co., overriding previous effects const blockCrit = new BooleanHolder(false); - applyAbAttrs("BlockCritAbAttr", this, null, false, blockCrit); + applyAbAttrs("BlockCritAbAttr", { pokemon: this, blockCrit }); const blockCritTag = globalScene.arena.getTagOnSide( NoCritTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, @@ -3998,7 +4074,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr */ if (!source || source.turnData.hitCount <= 1) { - applyPostDamageAbAttrs("PostDamageAbAttr", this, damage, this.hasPassive(), false, [], source); + applyAbAttrs("PostDamageAbAttr", { pokemon: this, damage, source }); } return damage; } @@ -4046,11 +4122,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const stubTag = new BattlerTag(tagType, 0, 0); const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, stubTag, cancelled, true); + applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: stubTag, cancelled, simulated: true }); const userField = this.getAlliedField(); userField.forEach(pokemon => - applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, stubTag, cancelled, true, this), + applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { + pokemon, + tag: stubTag, + cancelled, + simulated: true, + target: this, + }), ); return !cancelled.value; @@ -4066,13 +4148,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, newTag, cancelled); + applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: newTag, cancelled }); if (cancelled.value) { return false; } for (const pokemon of this.getAlliedField()) { - applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, newTag, cancelled, false, this); + applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { pokemon, tag: newTag, cancelled, target: this }); if (cancelled.value) { return false; } @@ -4100,7 +4182,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getTag(tagType: Constructor): T | undefined; getTag(tagType: BattlerTagType | Constructor): BattlerTag | undefined { - return tagType instanceof Function + return typeof tagType === "function" ? this.summonData.tags.find(t => t instanceof tagType) : this.summonData.tags.find(t => t.tagType === tagType); } @@ -4373,9 +4455,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { try { SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2))); - fusionCry = this.getFusionSpeciesForm(undefined, true).cry( - Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig), - ); + fusionCry = this.getFusionSpeciesForm(undefined, true).cry({ + seek: Math.max(fusionCry.totalDuration * 0.4, 0), + ...soundConfig, + }); SoundFade.fadeIn( scene, fusionCry, @@ -4517,13 +4600,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (i === transitionIndex && fusionCryKey) { SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2))); - fusionCry = globalScene.playSound( - fusionCryKey, - Object.assign({ - seek: Math.max(fusionCry.totalDuration * 0.4, 0), - rate: rate, - }), - ); + fusionCry = globalScene.playSound(fusionCryKey, { + seek: Math.max(fusionCry.totalDuration * 0.4, 0), + rate: rate, + }); SoundFade.fadeIn( globalScene, fusionCry, @@ -4597,7 +4677,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreField Whether any field effects (weather, terrain, etc.) should be considered */ canSetStatus( - effect: StatusEffect | undefined, + effect: StatusEffect, quiet = false, overrideStatus = false, sourcePokemon: Pokemon | null = null, @@ -4628,8 +4708,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity const cancelImmunity = new BooleanHolder(false); + // TODO: Determine if we need to pass `quiet` as the value for simulated in this call if (sourcePokemon) { - applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", sourcePokemon, cancelImmunity, false, effect, defType); + applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", { + pokemon: sourcePokemon, + cancelled: cancelImmunity, + statusEffect: effect, + defenderType: defType, + }); if (cancelImmunity.value) { return false; } @@ -4678,21 +4764,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const cancelled = new BooleanHolder(false); - applyPreSetStatusAbAttrs("StatusEffectImmunityAbAttr", this, effect, cancelled, quiet); + applyAbAttrs("StatusEffectImmunityAbAttr", { pokemon: this, effect, cancelled, simulated: quiet }); if (cancelled.value) { return false; } for (const pokemon of this.getAlliedField()) { - applyPreSetStatusAbAttrs( - "UserFieldStatusEffectImmunityAbAttr", + applyAbAttrs("UserFieldStatusEffectImmunityAbAttr", { pokemon, effect, cancelled, - quiet, - this, - sourcePokemon, - ); + simulated: quiet, + target: this, + source: sourcePokemon, + }); if (cancelled.value) { break; } @@ -4723,6 +4808,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { overrideStatus?: boolean, quiet = true, ): boolean { + if (!effect) { + return false; + } if (!this.canSetStatus(effect, quiet, overrideStatus, sourcePokemon)) { return false; } @@ -4781,7 +4869,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } sleepTurnsRemaining = sleepTurnsRemaining!; // tell TS compiler it's defined - effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call this.status = new Status(effect, 0, sleepTurnsRemaining?.value); return true; @@ -4842,7 +4929,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { const bypassed = new BooleanHolder(false); if (attacker) { - applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed); + applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed }); } return !bypassed.value; } @@ -5316,10 +5403,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { for (let sc = 0; sc < spriteColors.length; sc++) { const delta = Math.min(...paletteDeltas[sc]); - const paletteIndex = Math.min( - paletteDeltas[sc].findIndex(pd => pd === delta), - fusionPalette.length - 1, - ); + const paletteIndex = Math.min(paletteDeltas[sc].indexOf(delta), fusionPalette.length - 1); if (delta < 255) { const ratio = easeFunc(delta / 255); const color = [0, 0, 0, fusionSpriteColors[sc][3]]; @@ -5391,7 +5475,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.hideInfo(); } // Trigger abilities that activate upon leaving the field - applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", this); + applyAbAttrs("PreLeaveFieldAbAttr", { pokemon: this }); this.setSwitchOutStatus(true); globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); globalScene.field.remove(this, destroy); @@ -5451,7 +5535,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.removeModifier(heldItem, this.isEnemy()); } if (forBattle) { - applyPostItemLostAbAttrs("PostItemLostAbAttr", this, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon: this }); } return true; diff --git a/src/field/trainer.ts b/src/field/trainer.ts index b64821d259a..8ac896e2717 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -158,7 +158,7 @@ export default class Trainer extends Phaser.GameObjects.Container { * @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE. * @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false. * @returns {string} - The formatted name of the trainer. - **/ + */ getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle = false): string { // Get the base title based on the trainer slot and variant. let name = this.config.getTitle(trainerSlot, this.variant); diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 02a95f71ac4..388802f467e 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -70,20 +70,20 @@ const repeatInputDelayMillis = 250; * providing a unified interface for all input-related interactions. */ export class InputsController { - private gamepads: Array = new Array(); + private gamepads: Array = []; public events: Phaser.Events.EventEmitter; - private buttonLock: Button[] = new Array(); + private buttonLock: Button[] = []; private interactions: Map> = new Map(); private configs: Map = new Map(); public gamepadSupport = true; public selectedDevice; - private disconnectedGamepads: Array = new Array(); + private disconnectedGamepads: Array = []; public lastSource = "keyboard"; - private inputInterval: NodeJS.Timeout[] = new Array(); + private inputInterval: NodeJS.Timeout[] = []; private touchControls: TouchControl; public moveTouchControlsHandler: MoveTouchControlsHandler; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index a04a5e2be47..fcbe6b66a4e 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1585,7 +1585,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { pokemonEvolutions.hasOwnProperty(p.species.speciesId) && (!p.pauseEvolutions || p.species.speciesId === SpeciesId.SLOWPOKE || - p.species.speciesId === SpeciesId.EEVEE), + p.species.speciesId === SpeciesId.EEVEE || + p.species.speciesId === SpeciesId.KIRLIA || + p.species.speciesId === SpeciesId.SNORUNT), ) .flatMap(p => { const evolutions = pokemonEvolutions[p.species.speciesId]; @@ -1599,7 +1601,9 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && (!p.pauseEvolutions || p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE || - p.fusionSpecies.speciesId === SpeciesId.EEVEE), + p.fusionSpecies.speciesId === SpeciesId.EEVEE || + p.fusionSpecies.speciesId === SpeciesId.KIRLIA || + p.fusionSpecies.speciesId === SpeciesId.SNORUNT), ) .flatMap(p => { const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 54b7323569a..247b64ca2c0 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -42,7 +42,7 @@ import type { import { getModifierType } from "#app/utils/modifier-utils"; import { Color, ShadowColor } from "#enums/color"; import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters"; -import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types"; @@ -751,7 +751,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { } getPokemon(): Pokemon | undefined { - return this.pokemonId ? (globalScene.getPokemonById(this.pokemonId) ?? undefined) : undefined; + return globalScene.getPokemonById(this.pokemonId) ?? undefined; } getScoreMultiplier(): number { @@ -1879,7 +1879,7 @@ export class BerryModifier extends PokemonHeldItemModifier { // munch the berry and trigger unburden-like effects getBerryEffectFunc(this.berryType)(pokemon); - applyPostItemLostAbAttrs("PostItemLostAbAttr", pokemon, false); + applyAbAttrs("PostItemLostAbAttr", { pokemon }); // Update berry eaten trackers for Belch, Harvest, Cud Chew, etc. // Don't recover it if we proc berry pouch (no item duplication) @@ -1967,7 +1967,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { // Reapply Commander on the Pokemon's side of the field, if applicable const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { - applyAbAttrs("CommanderAbAttr", p, null, false); + applyAbAttrs("CommanderAbAttr", { pokemon: p }); } return true; } diff --git a/src/phase-manager.ts b/src/phase-manager.ts index 9390e6dd75d..a4256f110ef 100644 --- a/src/phase-manager.ts +++ b/src/phase-manager.ts @@ -100,7 +100,7 @@ import { UnlockPhase } from "#app/phases/unlock-phase"; import { VictoryPhase } from "#app/phases/victory-phase"; import { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; -/** +/* * Manager for phases used by battle scene. * * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.* diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 5e24f3474a6..ecd64380c31 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -1,4 +1,4 @@ -import { applyAbAttrs, applyPreLeaveFieldAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; @@ -25,10 +25,10 @@ export class AttemptRunPhase extends PokemonPhase { this.attemptRunAway(playerField, enemyField, escapeChance); - applyAbAttrs("RunSuccessAbAttr", playerPokemon, null, false, escapeChance); + applyAbAttrs("RunSuccessAbAttr", { pokemon: playerPokemon, chance: escapeChance }); if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) { - enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", enemyPokemon)); + enemyField.forEach(enemyPokemon => applyAbAttrs("PreLeaveFieldAbAttr", { pokemon: enemyPokemon })); globalScene.playSound("se/flee"); globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); @@ -38,14 +38,11 @@ export class AttemptRunPhase extends PokemonPhase { alpha: 0, duration: 250, ease: "Sine.easeIn", - onComplete: () => - // biome-ignore lint/complexity/noForEach: TODO - enemyField.forEach(enemyPokemon => enemyPokemon.destroy()), + onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()), }); globalScene.clearEnemyHeldItemModifiers(); - // biome-ignore lint/complexity/noForEach: TODO enemyField.forEach(enemyPokemon => { enemyPokemon.hideInfo().then(() => enemyPokemon.destroy()); enemyPokemon.hp = 0; diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index e1bf4c2296c..297e20cb445 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier"; import { BattlePhase } from "./battle-phase"; @@ -65,7 +65,7 @@ export class BattleEndPhase extends BattlePhase { } for (const pokemon of globalScene.getPokemonAllowedInBattle()) { - applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory); + applyAbAttrs("PostBattleAbAttr", { pokemon, victory: this.isVictory }); } if (globalScene.currentBattle.moneyScattered) { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index c126f3306b9..61124a7cda8 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -20,7 +20,7 @@ export class BerryPhase extends FieldPhase { this.executeForAll(pokemon => { this.eatBerries(pokemon); - applyAbAttrs("RepeatBerryNextTurnAbAttr", pokemon, null); + applyAbAttrs("CudChewConsumeBerryAbAttr", { pokemon }); }); this.end(); @@ -42,7 +42,7 @@ export class BerryPhase extends FieldPhase { // TODO: If both opponents on field have unnerve, which one displays its message? const cancelled = new BooleanHolder(false); - pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", opp, cancelled)); + pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", { pokemon: opp, cancelled })); if (cancelled.value) { globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:preventBerryUse", { @@ -70,6 +70,6 @@ export class BerryPhase extends FieldPhase { globalScene.updateModifiers(pokemon.isPlayer()); // AbilityId.CHEEK_POUCH only works once per round of nom noms - applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false)); + applyAbAttrs("HealFromBerryUseAbAttr", { pokemon }); } } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index f2c23384627..52c2b2e465d 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#enums/battler-index"; import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { applyAbAttrs, applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims"; import { getCharVariantFromDialogue } from "#app/data/dialogue"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -128,7 +128,7 @@ export class EncounterPhase extends BattlePhase { .slice(0, !battle.double ? 1 : 2) .reverse() .forEach(playerPokemon => { - applyAbAttrs("SyncEncounterNatureAbAttr", playerPokemon, null, false, battle.enemyParty[e]); + applyAbAttrs("SyncEncounterNatureAbAttr", { pokemon: playerPokemon, target: battle.enemyParty[e] }); }); } } @@ -249,7 +249,7 @@ export class EncounterPhase extends BattlePhase { if (e < (battle.double ? 2 : 1)) { if (battle.battleType === BattleType.WILD) { for (const pokemon of globalScene.getField()) { - applyPreSummonAbAttrs("PreSummonAbAttr", pokemon, []); + applyAbAttrs("PreSummonAbAttr", { pokemon }); } globalScene.field.add(enemyPokemon); battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index bcc93b028bd..8e4300986b3 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -23,6 +23,8 @@ export class EvolutionPhase extends Phase { protected pokemon: PlayerPokemon; protected lastLevel: number; + protected evoChain: Phaser.Tweens.TweenChain | null = null; + private preEvolvedPokemonName: string; private evolution: SpeciesFormEvolution | null; @@ -40,13 +42,23 @@ export class EvolutionPhase extends Phase { protected pokemonEvoSprite: Phaser.GameObjects.Sprite; protected pokemonEvoTintSprite: Phaser.GameObjects.Sprite; - constructor(pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: number) { + /** Whether the evolution can be cancelled by the player */ + protected canCancel: boolean; + + /** + * @param pokemon - The Pokemon that is evolving + * @param evolution - The form being evolved into + * @param lastLevel - The level at which the Pokemon is evolving + * @param canCancel - Whether the evolution can be cancelled by the player + */ + constructor(pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: number, canCancel = true) { super(); this.pokemon = pokemon; this.evolution = evolution; this.lastLevel = lastLevel; this.fusionSpeciesEvolved = evolution instanceof FusionSpeciesFormEvolution; + this.canCancel = canCancel; } validate(): boolean { @@ -57,198 +69,227 @@ export class EvolutionPhase extends Phase { return globalScene.ui.setModeForceTransition(UiMode.EVOLUTION_SCENE); } - start() { - super.start(); + /** + * Set up the following evolution assets + * - {@linkcode evolutionContainer} + * - {@linkcode evolutionBaseBg} + * - {@linkcode evolutionBg} + * - {@linkcode evolutionBgOverlay} + * - {@linkcode evolutionOverlay} + * + */ + private setupEvolutionAssets(): void { + this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneHandler; + this.evolutionContainer = this.evolutionHandler.evolutionContainer; + this.evolutionBaseBg = globalScene.add.image(0, 0, "default_bg").setOrigin(0); - this.setMode().then(() => { - if (!this.validate()) { - return this.end(); - } + this.evolutionBg = globalScene.add + .video(0, 0, "evo_bg") + .stop() + .setOrigin(0) + .setScale(0.4359673025) + .setVisible(false); - globalScene.fadeOutBgm(undefined, false); + this.evolutionBgOverlay = globalScene.add + .rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0x262626) + .setOrigin(0) + .setAlpha(0); + this.evolutionContainer.add([this.evolutionBaseBg, this.evolutionBgOverlay, this.evolutionBg]); - this.evolutionHandler = globalScene.ui.getHandler() as EvolutionSceneHandler; + this.evolutionOverlay = globalScene.add.rectangle( + 0, + -globalScene.game.canvas.height / 6, + globalScene.game.canvas.width / 6, + globalScene.game.canvas.height / 6 - 48, + 0xffffff, + ); + this.evolutionOverlay.setOrigin(0).setAlpha(0); + globalScene.ui.add(this.evolutionOverlay); + } - this.evolutionContainer = this.evolutionHandler.evolutionContainer; + /** + * Configure the sprite, setting its pipeline data + * @param pokemon - The pokemon object that the sprite information is configured from + * @param sprite - The sprite object to configure + * @param setPipeline - Whether to also set the pipeline; should be false + * if the sprite is only being updated with new sprite assets + * + * + * @returns The sprite object that was passed in + */ + protected configureSprite(pokemon: Pokemon, sprite: Phaser.GameObjects.Sprite, setPipeline = true): typeof sprite { + const spriteKey = pokemon.getSpriteKey(true); + try { + sprite.play(spriteKey); + } catch (err: unknown) { + console.error(`Failed to play animation for ${spriteKey}`, err); + } - this.evolutionBaseBg = globalScene.add.image(0, 0, "default_bg"); - this.evolutionBaseBg.setOrigin(0, 0); - this.evolutionContainer.add(this.evolutionBaseBg); - - this.evolutionBg = globalScene.add.video(0, 0, "evo_bg").stop(); - this.evolutionBg.setOrigin(0, 0); - this.evolutionBg.setScale(0.4359673025); - this.evolutionBg.setVisible(false); - this.evolutionContainer.add(this.evolutionBg); - - this.evolutionBgOverlay = globalScene.add.rectangle( - 0, - 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, - 0x262626, - ); - this.evolutionBgOverlay.setOrigin(0, 0); - this.evolutionBgOverlay.setAlpha(0); - this.evolutionContainer.add(this.evolutionBgOverlay); - - const getPokemonSprite = () => { - const ret = globalScene.addPokemonSprite( - this.pokemon, - this.evolutionBaseBg.displayWidth / 2, - this.evolutionBaseBg.displayHeight / 2, - "pkmn__sub", - ); - ret.setPipeline(globalScene.spritePipeline, { - tone: [0.0, 0.0, 0.0, 0.0], - ignoreTimeTint: true, - }); - return ret; - }; - - this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonEvoSprite = getPokemonSprite())); - this.evolutionContainer.add((this.pokemonEvoTintSprite = getPokemonSprite())); - - this.pokemonTintSprite.setAlpha(0); - this.pokemonTintSprite.setTintFill(0xffffff); - this.pokemonEvoSprite.setVisible(false); - this.pokemonEvoTintSprite.setVisible(false); - this.pokemonEvoTintSprite.setTintFill(0xffffff); - - this.evolutionOverlay = globalScene.add.rectangle( - 0, - -globalScene.game.canvas.height / 6, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6 - 48, - 0xffffff, - ); - this.evolutionOverlay.setOrigin(0, 0); - this.evolutionOverlay.setAlpha(0); - globalScene.ui.add(this.evolutionOverlay); - - [this.pokemonSprite, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = this.pokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); - } - - sprite.setPipeline(globalScene.spritePipeline, { - tone: [0.0, 0.0, 0.0, 0.0], - hasShadow: false, - teraColor: getTypeRgb(this.pokemon.getTeraType()), - isTerastallized: this.pokemon.isTerastallized, - }); - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - sprite.setPipelineData("shiny", this.pokemon.shiny); - sprite.setPipelineData("variant", this.pokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (this.pokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; - }); + if (setPipeline) { + sprite.setPipeline(globalScene.spritePipeline, { + tone: [0.0, 0.0, 0.0, 0.0], + hasShadow: false, + teraColor: getTypeRgb(pokemon.getTeraType()), + isTerastallized: pokemon.isTerastallized, + }); + } + + sprite + .setPipelineData("ignoreTimeTint", true) + .setPipelineData("spriteKey", pokemon.getSpriteKey()) + .setPipelineData("shiny", pokemon.shiny) + .setPipelineData("variant", pokemon.variant); + + for (let k of ["spriteColors", "fusionSpriteColors"]) { + if (pokemon.summonData.speciesForm) { + k += "Base"; + } + sprite.pipelineData[k] = pokemon.getSprite().pipelineData[k]; + } + + return sprite; + } + + private getPokemonSprite(): Phaser.GameObjects.Sprite { + const sprite = globalScene.addPokemonSprite( + this.pokemon, + this.evolutionBaseBg.displayWidth / 2, + this.evolutionBaseBg.displayHeight / 2, + "pkmn__sub", + ); + sprite.setPipeline(globalScene.spritePipeline, { + tone: [0.0, 0.0, 0.0, 0.0], + ignoreTimeTint: true, + }); + return sprite; + } + + /** + * Initialize {@linkcode pokemonSprite}, {@linkcode pokemonTintSprite}, {@linkcode pokemonEvoSprite}, and {@linkcode pokemonEvoTintSprite} + * and add them to the {@linkcode evolutionContainer} + */ + private setupPokemonSprites(): void { + this.pokemonSprite = this.configureSprite(this.pokemon, this.getPokemonSprite()); + this.pokemonTintSprite = this.configureSprite( + this.pokemon, + this.getPokemonSprite().setAlpha(0).setTintFill(0xffffff), + ); + this.pokemonEvoSprite = this.configureSprite(this.pokemon, this.getPokemonSprite().setVisible(false)); + this.pokemonEvoTintSprite = this.configureSprite( + this.pokemon, + this.getPokemonSprite().setVisible(false).setTintFill(0xffffff), + ); + + this.evolutionContainer.add([ + this.pokemonSprite, + this.pokemonTintSprite, + this.pokemonEvoSprite, + this.pokemonEvoTintSprite, + ]); + } + + async start() { + super.start(); + await this.setMode(); + + if (!this.validate()) { + return this.end(); + } + this.setupEvolutionAssets(); + this.setupPokemonSprites(); + this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon); + this.doEvolution(); + } + + /** + * Update the sprites depicting the evolved Pokemon + * @param evolvedPokemon - The evolved Pokemon + */ + private updateEvolvedPokemonSprites(evolvedPokemon: Pokemon): void { + this.configureSprite(evolvedPokemon, this.pokemonEvoSprite, false); + this.configureSprite(evolvedPokemon, this.pokemonEvoTintSprite, false); + } + + /** + * Adds the evolution tween and begins playing it + */ + private playEvolutionAnimation(evolvedPokemon: Pokemon): void { + globalScene.time.delayedCall(1000, () => { + this.evolutionBgm = globalScene.playSoundWithoutBgm("evolution"); + globalScene.tweens.add({ + targets: this.evolutionBgOverlay, + alpha: 1, + delay: 500, + duration: 1500, + ease: "Sine.easeOut", + onComplete: () => { + globalScene.time.delayedCall(1000, () => { + this.evolutionBg.setVisible(true).play(); + }); + globalScene.playSound("se/charge"); + this.doSpiralUpward(); + this.fadeOutPokemonSprite(evolvedPokemon); + }, }); - this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon); - this.doEvolution(); }); } + private fadeOutPokemonSprite(evolvedPokemon: Pokemon): void { + globalScene.tweens.addCounter({ + from: 0, + to: 1, + duration: 2000, + onUpdate: t => { + this.pokemonTintSprite.setAlpha(t.getValue()); + }, + onComplete: () => { + this.pokemonSprite.setVisible(false); + globalScene.time.delayedCall(1100, () => { + globalScene.playSound("se/beam"); + this.doArcDownward(); + this.prepareForCycle(evolvedPokemon); + }); + }, + }); + } + + /** + * Prepares the evolution cycle by setting up the tint sprites and starting the cycle + */ + private prepareForCycle(evolvedPokemon: Pokemon): void { + globalScene.time.delayedCall(1500, () => { + this.pokemonEvoTintSprite.setScale(0.25).setVisible(true); + this.evolutionHandler.canCancel = this.canCancel; + this.doCycle(1, undefined, () => { + if (this.evolutionHandler.cancelled) { + this.handleFailedEvolution(evolvedPokemon); + } else { + this.handleSuccessEvolution(evolvedPokemon); + } + }); + }); + } + + /** + * Show the evolution text and then commence the evolution animation + */ doEvolution(): void { globalScene.ui.showText( i18next.t("menu:evolving", { pokemonName: this.preEvolvedPokemonName }), null, () => { this.pokemon.cry(); - this.pokemon.getPossibleEvolution(this.evolution).then(evolvedPokemon => { - [this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = evolvedPokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); - } - - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", evolvedPokemon.getSpriteKey()); - sprite.setPipelineData("shiny", evolvedPokemon.shiny); - sprite.setPipelineData("variant", evolvedPokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (evolvedPokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = evolvedPokemon.getSprite().pipelineData[k]; - }); - }); - - globalScene.time.delayedCall(1000, () => { - this.evolutionBgm = globalScene.playSoundWithoutBgm("evolution"); - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 1, - delay: 500, - duration: 1500, - ease: "Sine.easeOut", - onComplete: () => { - globalScene.time.delayedCall(1000, () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - }); - this.evolutionBg.setVisible(true); - this.evolutionBg.play(); - }); - globalScene.playSound("se/charge"); - this.doSpiralUpward(); - globalScene.tweens.addCounter({ - from: 0, - to: 1, - duration: 2000, - onUpdate: t => { - this.pokemonTintSprite.setAlpha(t.getValue()); - }, - onComplete: () => { - this.pokemonSprite.setVisible(false); - globalScene.time.delayedCall(1100, () => { - globalScene.playSound("se/beam"); - this.doArcDownward(); - globalScene.time.delayedCall(1500, () => { - this.pokemonEvoTintSprite.setScale(0.25); - this.pokemonEvoTintSprite.setVisible(true); - this.evolutionHandler.canCancel = true; - this.doCycle(1).then(success => { - if (success) { - this.handleSuccessEvolution(evolvedPokemon); - } else { - this.handleFailedEvolution(evolvedPokemon); - } - }); - }); - }); - }, - }); - }, - }); - }); + this.updateEvolvedPokemonSprites(evolvedPokemon); + this.playEvolutionAnimation(evolvedPokemon); }); }, 1000, ); } - /** - * Handles a failed/stopped evolution - * @param evolvedPokemon - The evolved Pokemon - */ - private handleFailedEvolution(evolvedPokemon: Pokemon): void { - this.pokemonSprite.setVisible(true); - this.pokemonTintSprite.setScale(1); + /** Used exclusively by {@linkcode handleFailedEvolution} to fade out the evolution sprites and music */ + private fadeOutEvolutionAssets(): void { globalScene.tweens.add({ targets: [this.evolutionBg, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite], alpha: 0, @@ -257,9 +298,40 @@ export class EvolutionPhase extends Phase { this.evolutionBg.setVisible(false); }, }); - SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); + } + /** + * Show the confirmation prompt for pausing evolutions + * @param endCallback - The callback to call after either option is selected. + * This should end the evolution phase + */ + private showPauseEvolutionConfirmation(endCallback: () => void): void { + globalScene.ui.setOverlayMode( + UiMode.CONFIRM, + () => { + globalScene.ui.revertMode(); + this.pokemon.pauseEvolutions = true; + globalScene.ui.showText( + i18next.t("menu:evolutionsPaused", { + pokemonName: this.preEvolvedPokemonName, + }), + null, + endCallback, + 3000, + ); + }, + () => { + globalScene.ui.revertMode(); + globalScene.time.delayedCall(3000, endCallback); + }, + ); + } + + /** + * Used exclusively by {@linkcode handleFailedEvolution} to show the failed evolution UI messages + */ + private showFailedEvolutionUI(evolvedPokemon: Pokemon): void { globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); globalScene.ui.showText( @@ -280,25 +352,7 @@ export class EvolutionPhase extends Phase { evolvedPokemon.destroy(); this.end(); }; - globalScene.ui.setOverlayMode( - UiMode.CONFIRM, - () => { - globalScene.ui.revertMode(); - this.pokemon.pauseEvolutions = true; - globalScene.ui.showText( - i18next.t("menu:evolutionsPaused", { - pokemonName: this.preEvolvedPokemonName, - }), - null, - end, - 3000, - ); - }, - () => { - globalScene.ui.revertMode(); - globalScene.time.delayedCall(3000, end); - }, - ); + this.showPauseEvolutionConfirmation(end); }, ); }, @@ -307,6 +361,93 @@ export class EvolutionPhase extends Phase { ); } + /** + * Fade out the evolution assets, show the failed evolution UI messages, and enqueue the EndEvolutionPhase + * @param evolvedPokemon - The evolved Pokemon + */ + private handleFailedEvolution(evolvedPokemon: Pokemon): void { + this.pokemonSprite.setVisible(true); + this.pokemonTintSprite.setScale(1); + this.fadeOutEvolutionAssets(); + + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + this.showFailedEvolutionUI(evolvedPokemon); + } + + /** + * Fadeout evolution music, play the cry, show the evolution completed text, and end the phase + */ + private onEvolutionComplete(evolvedPokemon: Pokemon) { + SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); + globalScene.time.delayedCall(250, () => { + this.pokemon.cry(); + globalScene.time.delayedCall(1250, () => { + globalScene.playSoundWithoutBgm("evolution_fanfare"); + + evolvedPokemon.destroy(); + globalScene.ui.showText( + i18next.t("menu:evolutionDone", { + pokemonName: this.preEvolvedPokemonName, + evolvedPokemonName: this.pokemon.species.getExpandedSpeciesName(), + }), + null, + () => this.end(), + null, + true, + fixedInt(4000), + ); + globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm()); + }); + }); + } + + private postEvolve(evolvedPokemon: Pokemon): void { + const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved + ? LearnMoveSituation.EVOLUTION_FUSED + : this.pokemon.fusionSpecies + ? LearnMoveSituation.EVOLUTION_FUSED_BASE + : LearnMoveSituation.EVOLUTION; + const levelMoves = this.pokemon + .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) + .filter(lm => lm[0] === EVOLVE_MOVE); + for (const lm of levelMoves) { + globalScene.phaseManager.unshiftNew("LearnMovePhase", globalScene.getPlayerParty().indexOf(this.pokemon), lm[1]); + } + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + + globalScene.playSound("se/shine"); + this.doSpray(); + + globalScene.tweens.chain({ + targets: null, + tweens: [ + { + targets: this.evolutionOverlay, + alpha: 1, + duration: 250, + easing: "Sine.easeIn", + onComplete: () => { + this.evolutionBgOverlay.setAlpha(1); + this.evolutionBg.setVisible(false); + }, + }, + { + targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], + alpha: 0, + duration: 2000, + delay: 150, + easing: "Sine.easeIn", + }, + { + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + onComplete: () => this.onEvolutionComplete(evolvedPokemon), + }, + ], + }); + } + /** * Handles a successful evolution * @param evolvedPokemon - The evolved Pokemon @@ -316,85 +457,15 @@ export class EvolutionPhase extends Phase { this.pokemonEvoSprite.setVisible(true); this.doCircleInward(); - const onEvolutionComplete = () => { - SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); - globalScene.time.delayedCall(250, () => { - this.pokemon.cry(); - globalScene.time.delayedCall(1250, () => { - globalScene.playSoundWithoutBgm("evolution_fanfare"); - - evolvedPokemon.destroy(); - globalScene.ui.showText( - i18next.t("menu:evolutionDone", { - pokemonName: this.preEvolvedPokemonName, - evolvedPokemonName: this.pokemon.species.getExpandedSpeciesName(), - }), - null, - () => this.end(), - null, - true, - fixedInt(4000), - ); - globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm()); - }); - }); - }; - globalScene.time.delayedCall(900, () => { - this.evolutionHandler.canCancel = false; + this.evolutionHandler.canCancel = this.canCancel; - this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => { - const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved - ? LearnMoveSituation.EVOLUTION_FUSED - : this.pokemon.fusionSpecies - ? LearnMoveSituation.EVOLUTION_FUSED_BASE - : LearnMoveSituation.EVOLUTION; - const levelMoves = this.pokemon - .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) - .filter(lm => lm[0] === EVOLVE_MOVE); - for (const lm of levelMoves) { - globalScene.phaseManager.unshiftNew( - "LearnMovePhase", - globalScene.getPlayerParty().indexOf(this.pokemon), - lm[1], - ); - } - globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); - - globalScene.playSound("se/shine"); - this.doSpray(); - globalScene.tweens.add({ - targets: this.evolutionOverlay, - alpha: 1, - duration: 250, - easing: "Sine.easeIn", - onComplete: () => { - this.evolutionBgOverlay.setAlpha(1); - this.evolutionBg.setVisible(false); - globalScene.tweens.add({ - targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], - alpha: 0, - duration: 2000, - delay: 150, - easing: "Sine.easeIn", - onComplete: () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - onComplete: onEvolutionComplete, - }); - }, - }); - }, - }); - }); + this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => this.postEvolve(evolvedPokemon)); }); } doSpiralUpward() { let f = 0; - globalScene.tweens.addCounter({ repeat: 64, duration: getFrameMs(1), @@ -430,34 +501,41 @@ export class EvolutionPhase extends Phase { }); } - doCycle(l: number, lastCycle = 15): Promise { - return new Promise(resolve => { - const isLastCycle = l === lastCycle; - globalScene.tweens.add({ - targets: this.pokemonTintSprite, - scale: 0.25, + /** + * Return a tween chain that cycles the evolution sprites + */ + doCycle(cycles: number, lastCycle = 15, onComplete = () => {}): void { + // Make our tween start both at the same time + const tweens: Phaser.Types.Tweens.TweenBuilderConfig[] = []; + for (let i = cycles; i <= lastCycle; i += 0.5) { + tweens.push({ + targets: [this.pokemonTintSprite, this.pokemonEvoTintSprite], + scale: (_target, _key, _value, targetIndex: number, _totalTargets, _tween) => (targetIndex === 0 ? 0.25 : 1), ease: "Cubic.easeInOut", - duration: 500 / l, - yoyo: !isLastCycle, - }); - globalScene.tweens.add({ - targets: this.pokemonEvoTintSprite, - scale: 1, - ease: "Cubic.easeInOut", - duration: 500 / l, - yoyo: !isLastCycle, + duration: 500 / i, + yoyo: i !== lastCycle, onComplete: () => { if (this.evolutionHandler.cancelled) { - return resolve(false); + // cause the tween chain to complete instantly, skipping the remaining tweens. + this.pokemonEvoTintSprite.setScale(1); + this.pokemonEvoTintSprite.setVisible(false); + this.evoChain?.complete?.(); + return; } - if (l < lastCycle) { - this.doCycle(l + 0.5, lastCycle).then(success => resolve(success)); - } else { - this.pokemonTintSprite.setVisible(false); - resolve(true); + if (i === lastCycle) { + this.pokemonEvoTintSprite.setScale(1); } }, }); + } + + this.evoChain = globalScene.tweens.chain({ + targets: null, + tweens, + onComplete: () => { + this.evoChain = null; + onComplete(); + }, }); } diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 675a198d096..c2658b62b23 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -1,11 +1,7 @@ import type { BattlerIndex } from "#enums/battler-index"; import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; -import { - applyPostFaintAbAttrs, - applyPostKnockOutAbAttrs, - applyPostVictoryAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { battleSpecDialogue } from "#app/data/dialogue"; import { allMoves } from "#app/data/data-lists"; @@ -117,29 +113,31 @@ export class FaintPhase extends PokemonPhase { pokemon.resetTera(); + // TODO: this can be simplified by just checking whether lastAttack is defined if (pokemon.turnData.attacksReceived?.length) { const lastAttack = pokemon.turnData.attacksReceived[0]; - applyPostFaintAbAttrs( - "PostFaintAbAttr", - pokemon, - globalScene.getPokemonById(lastAttack.sourceId)!, - new PokemonMove(lastAttack.move).getMove(), - lastAttack.result, - ); // TODO: is this bang correct? + applyAbAttrs("PostFaintAbAttr", { + pokemon: pokemon, + // TODO: We should refactor lastAttack's sourceId to forbid null and just use undefined + attacker: globalScene.getPokemonById(lastAttack.sourceId) ?? undefined, + // TODO: improve the way that we provide the move that knocked out the pokemon... + move: new PokemonMove(lastAttack.move).getMove(), + hitResult: lastAttack.result, + }); // TODO: is this bang correct? } else { //If killed by indirect damage, apply post-faint abilities without providing a last move - applyPostFaintAbAttrs("PostFaintAbAttr", pokemon); + applyAbAttrs("PostFaintAbAttr", { pokemon }); } const alivePlayField = globalScene.getField(true); for (const p of alivePlayField) { - applyPostKnockOutAbAttrs("PostKnockOutAbAttr", p, pokemon); + applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon }); } if (pokemon.turnData.attacksReceived?.length) { const defeatSource = this.source; if (defeatSource?.isOnField()) { - applyPostVictoryAbAttrs("PostVictoryAbAttr", defeatSource); + applyAbAttrs("PostVictoryAbAttr", { pokemon: defeatSource }); const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move]; const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr"); if (pvattrs.length) { diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index 13cd410ef87..6d60cacd69d 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -3,7 +3,7 @@ import { fixedInt } from "#app/utils/common"; import { achvs } from "../system/achv"; import type { SpeciesFormChange } from "../data/pokemon-forms"; import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms/form-change-triggers"; -import type { PlayerPokemon } from "../field/pokemon"; +import type { default as Pokemon, PlayerPokemon } from "../field/pokemon"; import { UiMode } from "#enums/ui-mode"; import type PartyUiHandler from "../ui/party-ui-handler"; import { getPokemonNameWithAffix } from "../messages"; @@ -34,146 +34,158 @@ export class FormChangePhase extends EvolutionPhase { return globalScene.ui.setOverlayMode(UiMode.EVOLUTION_SCENE); } - doEvolution(): void { - const preName = getPokemonNameWithAffix(this.pokemon); - - this.pokemon.getPossibleForm(this.formChange).then(transformedPokemon => { - [this.pokemonEvoSprite, this.pokemonEvoTintSprite].map(sprite => { - const spriteKey = transformedPokemon.getSpriteKey(true); - try { - sprite.play(spriteKey); - } catch (err: unknown) { - console.error(`Failed to play animation for ${spriteKey}`, err); + /** + * Commence the tweens that play after the form change animation finishes + * @param transformedPokemon - The Pokemon after the evolution + * @param preName - The name of the Pokemon before the evolution + */ + private postFormChangeTweens(transformedPokemon: Pokemon, preName: string): void { + globalScene.tweens.chain({ + targets: null, + tweens: [ + { + targets: this.evolutionOverlay, + alpha: 1, + duration: 250, + easing: "Sine.easeIn", + onComplete: () => { + this.evolutionBgOverlay.setAlpha(1); + this.evolutionBg.setVisible(false); + }, + }, + { + targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], + alpha: 0, + duration: 2000, + delay: 150, + easing: "Sine.easeIn", + }, + { + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + completeDelay: 250, + onComplete: () => this.pokemon.cry(), + }, + ], + // 1.25 seconds after the pokemon cry + completeDelay: 1250, + onComplete: () => { + let playEvolutionFanfare = false; + if (this.formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1) { + globalScene.validateAchv(achvs.MEGA_EVOLVE); + playEvolutionFanfare = true; + } else if ( + this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || + this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1 + ) { + globalScene.validateAchv(achvs.GIGANTAMAX); + playEvolutionFanfare = true; } - sprite.setPipelineData("ignoreTimeTint", true); - sprite.setPipelineData("spriteKey", transformedPokemon.getSpriteKey()); - sprite.setPipelineData("shiny", transformedPokemon.shiny); - sprite.setPipelineData("variant", transformedPokemon.variant); - ["spriteColors", "fusionSpriteColors"].map(k => { - if (transformedPokemon.summonData.speciesForm) { - k += "Base"; - } - sprite.pipelineData[k] = transformedPokemon.getSprite().pipelineData[k]; - }); - }); + const delay = playEvolutionFanfare ? 4000 : 1750; + globalScene.playSoundWithoutBgm(playEvolutionFanfare ? "evolution_fanfare" : "minor_fanfare"); + transformedPokemon.destroy(); + globalScene.ui.showText( + getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), + null, + () => this.end(), + null, + true, + fixedInt(delay), + ); + globalScene.time.delayedCall(fixedInt(delay + 250), () => globalScene.playBgm()); + }, + }); + } - globalScene.time.delayedCall(250, () => { - globalScene.tweens.add({ + /** + * Commence the animations that occur once the form change evolution cycle ({@linkcode doCycle}) is complete + * + * @privateRemarks + * This would prefer {@linkcode doCycle} to be refactored and de-promisified so this can be moved into {@linkcode beginTweens} + * @param preName - The name of the Pokemon before the evolution + * @param transformedPokemon - The Pokemon being transformed into + */ + private afterCycle(preName: string, transformedPokemon: Pokemon): void { + globalScene.playSound("se/sparkle"); + this.pokemonEvoSprite.setVisible(true); + this.doCircleInward(); + globalScene.time.delayedCall(900, () => { + this.pokemon.changeForm(this.formChange).then(() => { + if (!this.modal) { + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); + } + globalScene.playSound("se/shine"); + this.doSpray(); + this.postFormChangeTweens(transformedPokemon, preName); + }); + }); + } + + /** + * Commence the sequence of tweens and events that occur during the evolution animation + * @param preName The name of the Pokemon before the evolution + * @param transformedPokemon The Pokemon after the evolution + */ + private beginTweens(preName: string, transformedPokemon: Pokemon): void { + globalScene.tweens.chain({ + // Starts 250ms after sprites have been configured + targets: null, + tweens: [ + // Step 1: Fade in the background overlay + { + delay: 250, targets: this.evolutionBgOverlay, alpha: 1, - delay: 500, duration: 1500, ease: "Sine.easeOut", + // We want the backkground overlay to fade out after it fades in onComplete: () => { - globalScene.time.delayedCall(1000, () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - }); - this.evolutionBg.setVisible(true); - this.evolutionBg.play(); + globalScene.tweens.add({ + targets: this.evolutionBgOverlay, + alpha: 0, + duration: 250, + delay: 1000, }); + this.evolutionBg.setVisible(true).play(); + }, + }, + // Step 2: Play the sounds and fade in the tint sprite + { + targets: this.pokemonTintSprite, + alpha: { from: 0, to: 1 }, + duration: 2000, + onStart: () => { globalScene.playSound("se/charge"); this.doSpiralUpward(); - globalScene.tweens.addCounter({ - from: 0, - to: 1, - duration: 2000, - onUpdate: t => { - this.pokemonTintSprite.setAlpha(t.getValue()); - }, - onComplete: () => { - this.pokemonSprite.setVisible(false); - globalScene.time.delayedCall(1100, () => { - globalScene.playSound("se/beam"); - this.doArcDownward(); - globalScene.time.delayedCall(1000, () => { - this.pokemonEvoTintSprite.setScale(0.25); - this.pokemonEvoTintSprite.setVisible(true); - this.doCycle(1, 1).then(_success => { - globalScene.playSound("se/sparkle"); - this.pokemonEvoSprite.setVisible(true); - this.doCircleInward(); - globalScene.time.delayedCall(900, () => { - this.pokemon.changeForm(this.formChange).then(() => { - if (!this.modal) { - globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); - } - - globalScene.playSound("se/shine"); - this.doSpray(); - globalScene.tweens.add({ - targets: this.evolutionOverlay, - alpha: 1, - duration: 250, - easing: "Sine.easeIn", - onComplete: () => { - this.evolutionBgOverlay.setAlpha(1); - this.evolutionBg.setVisible(false); - globalScene.tweens.add({ - targets: [this.evolutionOverlay, this.pokemonEvoTintSprite], - alpha: 0, - duration: 2000, - delay: 150, - easing: "Sine.easeIn", - onComplete: () => { - globalScene.tweens.add({ - targets: this.evolutionBgOverlay, - alpha: 0, - duration: 250, - onComplete: () => { - globalScene.time.delayedCall(250, () => { - this.pokemon.cry(); - globalScene.time.delayedCall(1250, () => { - let playEvolutionFanfare = false; - if (this.formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1) { - globalScene.validateAchv(achvs.MEGA_EVOLVE); - playEvolutionFanfare = true; - } else if ( - this.formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || - this.formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1 - ) { - globalScene.validateAchv(achvs.GIGANTAMAX); - playEvolutionFanfare = true; - } - - const delay = playEvolutionFanfare ? 4000 : 1750; - globalScene.playSoundWithoutBgm( - playEvolutionFanfare ? "evolution_fanfare" : "minor_fanfare", - ); - - transformedPokemon.destroy(); - globalScene.ui.showText( - getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), - null, - () => this.end(), - null, - true, - fixedInt(delay), - ); - globalScene.time.delayedCall(fixedInt(delay + 250), () => - globalScene.playBgm(), - ); - }); - }); - }, - }); - }, - }); - }, - }); - }); - }); - }); - }); - }); - }, - }); }, + onComplete: () => { + this.pokemonSprite.setVisible(false); + }, + }, + ], + + // Step 3: Commence the form change animation via doCycle then continue the animation chain with afterCycle + completeDelay: 1100, + onComplete: () => { + globalScene.playSound("se/beam"); + this.doArcDownward(); + globalScene.time.delayedCall(1000, () => { + this.pokemonEvoTintSprite.setScale(0.25).setVisible(true); + this.doCycle(1, 1, () => this.afterCycle(preName, transformedPokemon)); }); - }); + }, + }); + } + + doEvolution(): void { + const preName = getPokemonNameWithAffix(this.pokemon, false); + + this.pokemon.getPossibleForm(this.formChange).then(transformedPokemon => { + this.configureSprite(transformedPokemon, this.pokemonEvoSprite, false); + this.configureSprite(transformedPokemon, this.pokemonEvoTintSprite, false); + this.beginTweens(preName, transformedPokemon); }); } diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index eaf1e4f58d7..3f92f26b9b9 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -299,7 +299,7 @@ export class GameOverPhase extends BattlePhase { battleType: globalScene.currentBattle.battleType, trainer: globalScene.currentBattle.trainer ? new TrainerData(globalScene.currentBattle.trainer) : null, gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index d7da1ab996c..610d670dcb9 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -1,12 +1,6 @@ import { BattlerIndex } from "#enums/battler-index"; import { globalScene } from "#app/global-scene"; -import { - applyExecutedMoveAbAttrs, - applyPostAttackAbAttrs, - applyPostDamageAbAttrs, - applyPostDefendAbAttrs, - applyPreAttackAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { ConditionalProtectTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import { MoveAnim } from "#app/data/battle-anims"; @@ -322,7 +316,7 @@ export class MoveEffectPhase extends PokemonPhase { // Assume single target for multi hit applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount); // If Parental Bond is applicable, add another hit - applyPreAttackAbAttrs("AddSecondStrikeAbAttr", user, null, move, false, hitCount, null); + applyAbAttrs("AddSecondStrikeAbAttr", { pokemon: user, move, hitCount }); // If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount); // Set the user's relevant turnData fields to reflect the final hit count @@ -370,7 +364,7 @@ export class MoveEffectPhase extends PokemonPhase { // Add to the move history entry if (this.firstHit) { user.pushMoveHistory(this.moveHistoryEntry); - applyExecutedMoveAbAttrs("ExecutedMoveAbAttr", user); + applyAbAttrs("ExecutedMoveAbAttr", { pokemon: user }); } try { @@ -439,7 +433,7 @@ export class MoveEffectPhase extends PokemonPhase { * @param hitResult - The {@linkcode HitResult} of the attempted move */ protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void { - applyPostDefendAbAttrs("PostDefendAbAttr", target, user, this.move, hitResult); + applyAbAttrs("PostDefendAbAttr", { pokemon: target, opponent: user, move: this.move, hitResult }); target.lapseTags(BattlerTagLapseType.AFTER_HIT); } @@ -696,12 +690,9 @@ export class MoveEffectPhase extends PokemonPhase { * @param target - The {@linkcode Pokemon} to be removed */ protected removeTarget(target: Pokemon): void { - const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); + const targetIndex = this.targets.indexOf(target.getBattlerIndex()); if (targetIndex !== -1) { - this.targets.splice( - this.targets.findIndex(ind => ind === target.getBattlerIndex()), - 1, - ); + this.targets.splice(this.targets.indexOf(target.getBattlerIndex()), 1); } } @@ -808,7 +799,9 @@ export class MoveEffectPhase extends PokemonPhase { // Multi-hit check for Wimp Out/Emergency Exit if (user.turnData.hitCount > 1) { - applyPostDamageAbAttrs("PostDamageAbAttr", target, 0, target.hasPassive(), false, [], user); + // TODO: Investigate why 0 is being passed for damage amount here + // and then determing if refactoring `applyMove` to return the damage dealt is appropriate. + applyAbAttrs("PostDamageAbAttr", { pokemon: target, damage: 0, source: user }); } } } @@ -1002,7 +995,7 @@ export class MoveEffectPhase extends PokemonPhase { this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false); this.applyHeldItemFlinchCheck(user, target, dealsDamage); this.applyOnGetHitAbEffects(user, target, hitResult); - applyPostAttackAbAttrs("PostAttackAbAttr", user, target, this.move, hitResult); + applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult }); // We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens if (!user.isPlayer() && this.move.is("AttackMove")) { diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 8c8f2ac5239..7e1006c74e8 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { PokemonPhase } from "./pokemon-phase"; import type { BattlerIndex } from "#enums/battler-index"; -import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import type Pokemon from "#app/field/pokemon"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; export class MoveEndPhase extends PokemonPhase { public readonly phaseName = "MoveEndPhase"; @@ -30,7 +30,7 @@ export class MoveEndPhase extends PokemonPhase { globalScene.arena.setIgnoreAbilities(false); for (const target of this.targets) { if (target) { - applyPostSummonAbAttrs("PostSummonRemoveEffectAbAttr", target); + applyAbAttrs("PostSummonRemoveEffectAbAttr", { pokemon: target }); } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 698b99c5d6e..f4354395cc1 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#enums/battler-index"; import { globalScene } from "#app/global-scene"; -import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import type { DelayedAttackTag } from "#app/data/arena-tag"; import { CommonAnim } from "#enums/move-anims-common"; import { CenterOfAttentionTag } from "#app/data/battler-tags"; @@ -228,14 +228,11 @@ export class MovePhase extends BattlePhase { case StatusEffect.SLEEP: { applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove()); const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0); - applyAbAttrs( - "ReduceStatusEffectDurationAbAttr", - this.pokemon, - null, - false, - this.pokemon.status.effect, - turnsRemaining, - ); + applyAbAttrs("ReduceStatusEffectDurationAbAttr", { + pokemon: this.pokemon, + statusEffect: this.pokemon.status.effect, + duration: turnsRemaining, + }); this.pokemon.status.sleepTurnsRemaining = turnsRemaining.value; healed = this.pokemon.status.sleepTurnsRemaining <= 0; activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP); @@ -363,16 +360,19 @@ export class MovePhase extends BattlePhase { this.executeMove(); } + /** Execute the current move and apply its effects. */ private executeMove() { const move = this.move.getMove(); + const targets = this.getActiveTargetPokemon(); + + // Update the battle's "last move" pointer unless we're currently mimicking a move or triggering Dancer. if (!move.hasAttr("CopyMoveAttr") && !isReflected(this.useMode)) { - // Update the battle's "last move" pointer unless we're currently mimicking a move or triggering Dancer. - // TODO: Research how Copycat interacts with the final attacking turn of Future Sight and co. globalScene.currentBattle.lastMove = move.id; } - // trigger ability-based user type changes, display move text and then execute move effects. - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, move); + // Trigger ability-based user type changes, display move text and then execute move effects. + // TODO: Investigate whether PokemonTypeChangeAbAttr can drop the "opponent" parameter + applyAbAttrs("PokemonTypeChangeAbAttr", { pokemon: this.pokemon, move, opponent: targets[0] }); this.showMoveText(); globalScene.phaseManager.unshiftNew( "MoveEffectPhase", @@ -384,10 +384,11 @@ export class MovePhase extends BattlePhase { // Handle Dancer, which triggers immediately after a move is used (rather than waiting on `this.end()`). // Note the MoveUseMode check here prevents an infinite Dancer loop. + // TODO: This needs to go at the end of `MoveEffectPhase` to check move results const dancerModes: MoveUseMode[] = [MoveUseMode.INDIRECT, MoveUseMode.REFLECTED] as const; if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !dancerModes.includes(this.useMode)) { globalScene.getField(true).forEach(pokemon => { - applyPostMoveUsedAbAttrs("PostMoveUsedAbAttr", pokemon, this.move, this.pokemon, this.targets); + applyAbAttrs("PostMoveUsedAbAttr", { pokemon, move: this.move, source: this.pokemon, targets: this.targets }); }); } } @@ -403,18 +404,22 @@ export class MovePhase extends BattlePhase { const move = this.move.getMove(); const targets = this.getActiveTargetPokemon(); - // NOTE: DO NOT CHANGE THE ORDER OF OPERATIONS HERE. + // DO NOT CHANGE THE ORDER OF OPERATIONS HERE! // Protean is supposed to trigger its effects first, _then_ move text is displayed, // _then_ any blockage messages are shown. // Roar, Whirlwind, Trick-or-Treat, and Forest's Curse will trigger Protean/Libero // even on failure, as will all moves blocked by terrain. - // TODO: Verify if this also applies to primal weather failures? + // TODO: Verify if this also applies to primal weather failures if ( failedDueToTerrain || [MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId) ) { - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, move); + applyAbAttrs("PokemonTypeChangeAbAttr", { + pokemon: this.pokemon, + move: this.move.getMove(), + opponent: targets[0], + }); } if (showText) { @@ -431,7 +436,7 @@ export class MovePhase extends BattlePhase { // Use move-specific failure messages if present before checking terrain/weather blockage // and falling back to the classic "But it failed!". const failureMessage = - move.getFailedText(this.pokemon, targets[0], move) ?? + move.getFailedText(this.pokemon, targets[0], move) || (failedDueToTerrain ? getTerrainBlockMessage(targets[0], globalScene.arena.getTerrainType()) : failedDueToWeather @@ -458,7 +463,11 @@ export class MovePhase extends BattlePhase { } // Protean and Libero apply on the charging turn of charge moves, even before showing usage text - applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove()); + applyAbAttrs("PokemonTypeChangeAbAttr", { + pokemon: this.pokemon, + move: this.move.getMove(), + opponent: targets[0], + }); this.showMoveText(); globalScene.phaseManager.unshiftNew( @@ -492,8 +501,8 @@ export class MovePhase extends BattlePhase { */ public getPpIncreaseFromPressure(targets: Pokemon[]): number { const foesWithPressure = this.pokemon - .getOpponents() - .filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr("IncreasePpAbAttr")); + .getOpponents(true) + .filter(o => targets.includes(o) && o.hasAbilityWithAttr("IncreasePpAbAttr")); return foesWithPressure.length; } @@ -503,62 +512,71 @@ export class MovePhase extends BattlePhase { * - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`). */ protected resolveRedirectTarget(): void { - if (this.targets.length === 1) { - const currentTarget = this.targets[0]; - const redirectTarget = new NumberHolder(currentTarget); + if (this.targets.length !== 1) { + // Spread moves cannot be redirected + return; + } - // check move redirection abilities of every pokemon *except* the user. - globalScene - .getField(true) - .filter(p => p !== this.pokemon) - .forEach(p => - applyAbAttrs("RedirectMoveAbAttr", p, null, false, this.move.moveId, redirectTarget, this.pokemon), - ); + const currentTarget = this.targets[0]; + const redirectTarget = new NumberHolder(currentTarget); - /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ - let redirectedByAbility = currentTarget !== redirectTarget.value; + // check move redirection abilities of every pokemon *except* the user. + globalScene + .getField(true) + .filter(p => p !== this.pokemon) + .forEach(pokemon => { + applyAbAttrs("RedirectMoveAbAttr", { + pokemon, + moveId: this.move.moveId, + targetIndex: redirectTarget, + sourcePokemon: this.pokemon, + }); + }); - // check for center-of-attention tags (note that this will override redirect abilities) - this.pokemon.getOpponents().forEach(p => { - const redirectTag = p.getTag(CenterOfAttentionTag); + /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ + let redirectedByAbility = currentTarget !== redirectTarget.value; - // TODO: don't hardcode this interaction. - // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) - if ( - redirectTag && - (!redirectTag.powder || - (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT))) - ) { - redirectTarget.value = p.getBattlerIndex(); - redirectedByAbility = false; + // check for center-of-attention tags (note that this will override redirect abilities) + this.pokemon.getOpponents(true).forEach(p => { + const redirectTag = p.getTag(CenterOfAttentionTag); + + // TODO: don't hardcode this interaction. + // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) + if ( + redirectTag && + (!redirectTag.powder || + (!this.pokemon.isOfType(PokemonType.GRASS) && !this.pokemon.hasAbility(AbilityId.OVERCOAT))) + ) { + redirectTarget.value = p.getBattlerIndex(); + redirectedByAbility = false; + } + }); + + // TODO: Don't hardcode these ability interactions + if (currentTarget !== redirectTarget.value) { + const bypassRedirectAttrs = this.move.getMove().getAttrs("BypassRedirectAttr"); + bypassRedirectAttrs.forEach(attr => { + if (!attr.abilitiesOnly || redirectedByAbility) { + redirectTarget.value = currentTarget; } }); - if (currentTarget !== redirectTarget.value) { - const bypassRedirectAttrs = this.move.getMove().getAttrs("BypassRedirectAttr"); - bypassRedirectAttrs.forEach(attr => { - if (!attr.abilitiesOnly || redirectedByAbility) { - redirectTarget.value = currentTarget; - } - }); - - if (this.pokemon.hasAbilityWithAttr("BlockRedirectAbAttr")) { - redirectTarget.value = currentTarget; - // TODO: Ability displays should be handled by the ability - globalScene.phaseManager.queueAbilityDisplay( - this.pokemon, - this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), - true, - ); - globalScene.phaseManager.queueAbilityDisplay( - this.pokemon, - this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), - false, - ); - } - - this.targets[0] = redirectTarget.value; + if (this.pokemon.hasAbilityWithAttr("BlockRedirectAbAttr")) { + redirectTarget.value = currentTarget; + // TODO: Ability displays should be handled by the ability + globalScene.phaseManager.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), + true, + ); + globalScene.phaseManager.queueAbilityDisplay( + this.pokemon, + this.pokemon.getPassiveAbility().hasAttr("BlockRedirectAbAttr"), + false, + ); } + + this.targets[0] = redirectTarget.value; } } @@ -574,7 +592,8 @@ export class MovePhase extends BattlePhase { return; } - if (this.pokemon.turnData.attacksReceived.length) { + // TODO: This should be covered in move conditions + if (this.pokemon.turnData.attacksReceived.length === 0) { this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; // account for metal burst and comeuppance hitting remaining targets in double battles @@ -616,12 +635,13 @@ export class MovePhase extends BattlePhase { } if (this.failed) { + // TODO: should this consider struggle? const ppUsed = isIgnorePP(this.useMode) ? 0 : 1; this.move.usePp(ppUsed); globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); } - if (this.cancelled && this.pokemon.summonData.tags?.find(t => t.tagType === BattlerTagType.FRENZY)) { + if (this.cancelled && this.pokemon.summonData.tags.some(t => t.tagType === BattlerTagType.FRENZY)) { frenzyMissFunc(this.pokemon, this.move.getMove()); } diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index 5aad607764f..74476412401 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -14,7 +14,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { if (pokemon) { pokemon.resetBattleAndWaveData(); if (pokemon.isOnField()) { - applyAbAttrs("PostBiomeChangeAbAttr", pokemon, null); + applyAbAttrs("PostBiomeChangeAbAttr", { pokemon }); } } } diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index dc26d070029..78db8ae0a99 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -8,7 +8,7 @@ import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers"; -import { applyPostSetStatusAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { isNullOrUndefined } from "#app/utils/common"; export class ObtainStatusEffectPhase extends PokemonPhase { @@ -53,7 +53,11 @@ export class ObtainStatusEffectPhase extends PokemonPhase { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true); // If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards globalScene.arena.setIgnoreAbilities(false); - applyPostSetStatusAbAttrs("PostSetStatusAbAttr", pokemon, this.statusEffect, this.sourcePokemon); + applyAbAttrs("PostSetStatusAbAttr", { + pokemon, + effect: this.statusEffect, + sourcePokemon: this.sourcePokemon ?? undefined, + }); } this.end(); }); diff --git a/src/phases/post-summon-activate-ability-phase.ts b/src/phases/post-summon-activate-ability-phase.ts index ba6c80d4ee0..b1079a9b3e5 100644 --- a/src/phases/post-summon-activate-ability-phase.ts +++ b/src/phases/post-summon-activate-ability-phase.ts @@ -1,4 +1,4 @@ -import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { PostSummonPhase } from "#app/phases/post-summon-phase"; import type { BattlerIndex } from "#enums/battler-index"; @@ -16,7 +16,8 @@ export class PostSummonActivateAbilityPhase extends PostSummonPhase { } start() { - applyPostSummonAbAttrs("PostSummonAbAttr", this.getPokemon(), this.passive, false); + // TODO: Check with Dean on whether or not passive must be provided to `this.passive` + applyAbAttrs("PostSummonAbAttr", { pokemon: this.getPokemon(), passive: this.passive }); this.end(); } diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 26fffd1b024..7f22148fdcf 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -28,7 +28,7 @@ export class PostSummonPhase extends PokemonPhase { const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { - applyAbAttrs("CommanderAbAttr", p, null, false); + applyAbAttrs("CommanderAbAttr", { pokemon: p }); } this.end(); diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index e0a3bb5c00b..fd7dd6ed419 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#enums/battler-index"; -import { applyAbAttrs, applyPostDamageAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { CommonBattleAnim } from "#app/data/battle-anims"; import { CommonAnim } from "#enums/move-anims-common"; import { getStatusEffectActivationText } from "#app/data/status-effect"; @@ -22,8 +22,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) { pokemon.status.incrementTurn(); const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); - applyAbAttrs("BlockStatusDamageAbAttr", pokemon, cancelled); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); + applyAbAttrs("BlockStatusDamageAbAttr", { pokemon, cancelled }); if (!cancelled.value) { globalScene.phaseManager.queueMessage( @@ -39,14 +39,14 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { break; case StatusEffect.BURN: damage.value = Math.max(pokemon.getMaxHp() >> 4, 1); - applyAbAttrs("ReduceBurnDamageAbAttr", pokemon, null, false, damage); + applyAbAttrs("ReduceBurnDamageAbAttr", { pokemon, burnDamage: damage }); break; } if (damage.value) { // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ... globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true)); pokemon.updateInfo(); - applyPostDamageAbAttrs("PostDamageAbAttr", pokemon, damage.value, pokemon.hasPassive(), false, []); + applyAbAttrs("PostDamageAbAttr", { pokemon, damage: damage.value }); } new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end()); } else { diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 41b691844bf..9c4a0638b54 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -181,9 +181,10 @@ export class QuietFormChangePhase extends BattlePhase { } } if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) { - applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", this.pokemon, null); - applyAbAttrs("ClearWeatherAbAttr", this.pokemon, null); - applyAbAttrs("ClearTerrainAbAttr", this.pokemon, null); + const params = { pokemon: this.pokemon }; + applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", params); + applyAbAttrs("ClearWeatherAbAttr", params); + applyAbAttrs("ClearTerrainAbAttr", params); } super.end(); diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index e73f72f7a63..77fb7b38600 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -1,10 +1,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#enums/battler-index"; -import { - applyAbAttrs, - applyPostStatStageChangeAbAttrs, - applyPreStatStageChangeAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { MistTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#enums/arena-tag-side"; import type { ArenaTag } from "#app/data/arena-tag"; @@ -18,6 +14,10 @@ import { PokemonPhase } from "./pokemon-phase"; import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; import { OctolockTag } from "#app/data/battler-tags"; import { ArenaTagType } from "#app/enums/arena-tag-type"; +import type { + ConditionalUserFieldProtectStatAbAttrParams, + PreStatStageChangeAbAttrParams, +} from "#app/@types/ability-types"; export type StatStageChangeCallback = ( target: Pokemon | null, @@ -126,7 +126,7 @@ export class StatStageChangePhase extends PokemonPhase { const stages = new NumberHolder(this.stages); if (!this.ignoreAbilities) { - applyAbAttrs("StatStageChangeMultiplierAbAttr", pokemon, null, false, stages); + applyAbAttrs("StatStageChangeMultiplierAbAttr", { pokemon, numStages: stages }); } let simulate = false; @@ -146,42 +146,38 @@ export class StatStageChangePhase extends PokemonPhase { } if (!cancelled.value && !this.selfTarget && stages.value < 0) { - applyPreStatStageChangeAbAttrs("ProtectStatAbAttr", pokemon, stat, cancelled, simulate); - applyPreStatStageChangeAbAttrs( - "ConditionalUserFieldProtectStatAbAttr", + const abAttrParams: PreStatStageChangeAbAttrParams & ConditionalUserFieldProtectStatAbAttrParams = { pokemon, stat, cancelled, - simulate, - pokemon, - ); + simulated: simulate, + target: pokemon, + stages: this.stages, + }; + applyAbAttrs("ProtectStatAbAttr", abAttrParams); + applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", abAttrParams); + // TODO: Consider skipping this call if `cancelled` is false. const ally = pokemon.getAlly(); if (!isNullOrUndefined(ally)) { - applyPreStatStageChangeAbAttrs( - "ConditionalUserFieldProtectStatAbAttr", - ally, - stat, - cancelled, - simulate, - pokemon, - ); + applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", { ...abAttrParams, pokemon: ally }); } /** Potential stat reflection due to Mirror Armor, does not apply to Octolock end of turn effect */ if ( opponentPokemon !== undefined && + // TODO: investigate whether this is stoping mirror armor from applying to non-octolock + // reasons for stat drops if the user has the Octolock tag !pokemon.findTag(t => t instanceof OctolockTag) && !this.comingFromMirrorArmorUser ) { - applyPreStatStageChangeAbAttrs( - "ReflectStatStageChangeAbAttr", + applyAbAttrs("ReflectStatStageChangeAbAttr", { pokemon, stat, cancelled, - simulate, - opponentPokemon, - this.stages, - ); + simulated: simulate, + source: opponentPokemon, + stages: this.stages, + }); } } @@ -222,17 +218,16 @@ export class StatStageChangePhase extends PokemonPhase { if (stages.value > 0 && this.canBeCopied) { for (const opponent of pokemon.getOpponents()) { - applyAbAttrs("StatStageChangeCopyAbAttr", opponent, null, false, this.stats, stages.value); + applyAbAttrs("StatStageChangeCopyAbAttr", { pokemon: opponent, stats: this.stats, numStages: stages.value }); } } - applyPostStatStageChangeAbAttrs( - "PostStatStageChangeAbAttr", + applyAbAttrs("PostStatStageChangeAbAttr", { pokemon, - filteredStats, - this.stages, - this.selfTarget, - ); + stats: filteredStats, + stages: this.stages, + selfTarget: this.selfTarget, + }); // Look for any other stat change phases; if this is the last one, do White Herb check const existingPhase = globalScene.phaseManager.findPhase( diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index ad93452331f..95e4367d8df 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -10,7 +10,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; export class SummonPhase extends PartyMemberPokemonPhase { @@ -27,7 +27,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { start() { super.start(); - applyPreSummonAbAttrs("PreSummonAbAttr", this.getPokemon()); + applyAbAttrs("PreSummonAbAttr", { pokemon: this.getPokemon() }); this.preSummon(); } diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index af03cc42b54..ccd0681c068 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { applyPreSummonAbAttrs, applyPreSwitchOutAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { allMoves } from "#app/data/data-lists"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers"; @@ -44,7 +44,7 @@ export class SwitchSummonPhase extends SummonPhase { preSummon(): void { if (!this.player) { if (this.slotIndex === -1) { - //@ts-ignore + //@ts-expect-error this.slotIndex = globalScene.currentBattle.trainer?.getNextSummonIndex( !this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER, ); // TODO: what would be the default trainer-slot fallback? @@ -124,8 +124,8 @@ export class SwitchSummonPhase extends SummonPhase { switchedInPokemon.resetSummonData(); switchedInPokemon.loadAssets(true); - applyPreSummonAbAttrs("PreSummonAbAttr", switchedInPokemon); - applyPreSwitchOutAbAttrs("PreSwitchOutAbAttr", this.lastPokemon); + applyAbAttrs("PreSummonAbAttr", { pokemon: switchedInPokemon }); + applyAbAttrs("PreSwitchOutAbAttr", { pokemon: this.lastPokemon }); if (!switchedInPokemon) { this.end(); return; diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index ab46292c1d2..b5e56f6d63f 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -1,4 +1,4 @@ -import { applyPostTurnAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { TerrainType } from "#app/data/terrain"; import { WeatherType } from "#app/enums/weather-type"; @@ -49,7 +49,7 @@ export class TurnEndPhase extends FieldPhase { globalScene.applyModifier(EnemyStatusEffectHealChanceModifier, false, pokemon); } - applyPostTurnAbAttrs("PostTurnAbAttr", pokemon); + applyAbAttrs("PostTurnAbAttr", { pokemon }); } globalScene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 6219907fb68..2c4f2ead82e 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -66,8 +66,12 @@ export class TurnStartPhase extends FieldPhase { globalScene.getField(true).forEach(p => { const bypassSpeed = new BooleanHolder(false); const canCheckHeldItems = new BooleanHolder(true); - applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed); - applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems); + applyAbAttrs("BypassSpeedChanceAbAttr", { pokemon: p, bypass: bypassSpeed }); + applyAbAttrs("PreventBypassSpeedChanceAbAttr", { + pokemon: p, + bypass: bypassSpeed, + canCheckHeldItems: canCheckHeldItems, + }); if (canCheckHeldItems.value) { globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); } diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index d9239220376..5476ac67672 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -1,9 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { - applyPreWeatherEffectAbAttrs, - applyAbAttrs, - applyPostWeatherLapseAbAttrs, -} from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { CommonAnim } from "#enums/move-anims-common"; import type { Weather } from "#app/data/weather"; import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather"; @@ -41,15 +37,15 @@ export class WeatherEffectPhase extends CommonAnimPhase { const cancelled = new BooleanHolder(false); this.executeForAll((pokemon: Pokemon) => - applyPreWeatherEffectAbAttrs("SuppressWeatherEffectAbAttr", pokemon, this.weather, cancelled), + applyAbAttrs("SuppressWeatherEffectAbAttr", { pokemon, weather: this.weather, cancelled }), ); if (!cancelled.value) { const inflictDamage = (pokemon: Pokemon) => { const cancelled = new BooleanHolder(false); - applyPreWeatherEffectAbAttrs("PreWeatherDamageAbAttr", pokemon, this.weather, cancelled); - applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled); + applyAbAttrs("PreWeatherDamageAbAttr", { pokemon, weather: this.weather, cancelled }); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled }); if ( cancelled.value || @@ -80,7 +76,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { globalScene.ui.showText(getWeatherLapseMessage(this.weather.weatherType) ?? "", null, () => { this.executeForAll((pokemon: Pokemon) => { if (!pokemon.switchOutStatus) { - applyPostWeatherLapseAbAttrs("PostWeatherLapseAbAttr", pokemon, this.weather); + applyAbAttrs("PostWeatherLapseAbAttr", { pokemon, weather: this.weather }); } }); diff --git a/src/plugins/api/api-base.ts b/src/plugins/api/api-base.ts index f55ffe2d3fd..c7ff4619187 100644 --- a/src/plugins/api/api-base.ts +++ b/src/plugins/api/api-base.ts @@ -33,7 +33,7 @@ export abstract class ApiBase { * @param dataType The data-type of the {@linkcode bodyData}. */ protected async doPost(path: string, bodyData?: D, dataType: DataType = "json") { - let body: string | undefined = undefined; + let body: string | undefined; const headers: HeadersInit = {}; if (bodyData) { diff --git a/src/sprites/pokemon-sprite.ts b/src/sprites/pokemon-sprite.ts index 66432f5a4ea..cdf9e3acfa6 100644 --- a/src/sprites/pokemon-sprite.ts +++ b/src/sprites/pokemon-sprite.ts @@ -9,7 +9,7 @@ import type BattleScene from "#app/battle-scene"; // Regex patterns /** Regex matching double underscores */ -const DUNDER_REGEX = /\_{2}/g; +const DUNDER_REGEX = /_{2}/g; /** * Calculate the sprite ID from a pokemon form. diff --git a/src/sprites/sprite-utils.ts b/src/sprites/sprite-utils.ts index 0f4adf7882f..563bb1235b5 100644 --- a/src/sprites/sprite-utils.ts +++ b/src/sprites/sprite-utils.ts @@ -1,6 +1,6 @@ import { expSpriteKeys } from "#app/sprites/sprite-keys"; -const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/; +const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(-.*?)?(?:_[1-3])?$/; export function hasExpSprite(key: string): boolean { const keyMatch = expKeyRegex.exec(key); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index e933c5704f9..d5d4256f7d0 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -39,7 +39,7 @@ import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/ import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { setSettingKeyboard } from "#app/system/settings/settings-keyboard"; import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; -// biome-ignore lint/style/noNamespaceImport: Something weird is going on here and I don't want to touch it +// biome-ignore lint/performance/noNamespaceImport: Something weird is going on here and I don't want to touch it import * as Modifier from "#app/modifier/modifier"; import { StatusEffect } from "#enums/status-effect"; import ChallengeData from "#app/system/challenge-data"; @@ -300,7 +300,7 @@ export class GameData { voucherCounts: this.voucherCounts, eggs: this.eggs.map(e => new EggData(e)), gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), eggPity: this.eggPity.slice(0), unlockPity: this.unlockPity.slice(0), }; @@ -930,7 +930,7 @@ export class GameData { ? new TrainerData(globalScene.currentBattle.trainer) : null, gameVersion: globalScene.game.config.gameVersion, - timestamp: new Date().getTime(), + timestamp: Date.now(), challenges: globalScene.gameMode.challenges.map(c => new ChallengeData(c)), mysteryEncounterType: globalScene.currentBattle.mysteryEncounter?.encounterType ?? -1, mysteryEncounterSaveData: globalScene.mysteryEncounterSaveData, @@ -939,7 +939,7 @@ export class GameData { } getSession(slotId: number): Promise { - // biome-ignore lint/suspicious/noAsyncPromiseExecutor: + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this return new Promise(async (resolve, reject) => { if (slotId < 0) { return resolve(null); @@ -980,7 +980,7 @@ export class GameData { } loadSession(slotId: number, sessionData?: SessionSaveData): Promise { - // biome-ignore lint/suspicious/noAsyncPromiseExecutor: + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this return new Promise(async (resolve, reject) => { try { const initSessionFromData = async (sessionData: SessionSaveData) => { @@ -1610,7 +1610,7 @@ export class GameData { } } - this.defaultDexData = Object.assign({}, data); + this.defaultDexData = { ...data }; this.dexData = data; } diff --git a/src/system/game-speed.ts b/src/system/game-speed.ts index 712870dfaf1..207a4fb44a1 100644 --- a/src/system/game-speed.ts +++ b/src/system/game-speed.ts @@ -5,9 +5,13 @@ import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { FixedInt } from "#app/utils/common"; +type TweenManager = typeof Phaser.Tweens.TweenManager.prototype; + +/** The set of properties to mutate */ +const PROPERTIES = ["delay", "completeDelay", "loopDelay", "duration", "repeatDelay", "hold", "startDelay"]; + type FadeInType = typeof FadeIn; type FadeOutType = typeof FadeOut; - export function initGameSpeed() { const thisArg = this as BattleScene; @@ -18,14 +22,44 @@ export function initGameSpeed() { return thisArg.gameSpeed === 1 ? value : Math.ceil((value /= thisArg.gameSpeed)); }; - const originalAddEvent = this.time.addEvent; + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Complexity is necessary here + const mutateProperties = (obj: any, allowArray = false) => { + // We do not mutate Tweens or TweenChain objects themselves. + if (obj instanceof Phaser.Tweens.Tween || obj instanceof Phaser.Tweens.TweenChain) { + return; + } + // If allowArray is true then check if first obj is an array and if so, mutate the tweens inside + if (allowArray && Array.isArray(obj)) { + for (const tween of obj) { + mutateProperties(tween); + } + return; + } + + for (const prop of PROPERTIES) { + const objProp = obj[prop]; + if (typeof objProp === "number" || objProp instanceof FixedInt) { + obj[prop] = transformValue(objProp); + } + } + // If the object has a 'tweens' property that is an array, then it is a tween chain + // and we need to mutate its properties as well + if (obj.tweens && Array.isArray(obj.tweens)) { + for (const tween of obj.tweens) { + mutateProperties(tween); + } + } + }; + + const originalAddEvent: typeof Phaser.Time.Clock.prototype.addEvent = this.time.addEvent; this.time.addEvent = function (config: Phaser.Time.TimerEvent | Phaser.Types.Time.TimerEventConfig) { if (!(config instanceof Phaser.Time.TimerEvent) && config.delay) { config.delay = transformValue(config.delay); } return originalAddEvent.apply(this, [config]); }; - const originalTweensAdd = this.tweens.add; + const originalTweensAdd: TweenManager["add"] = this.tweens.add; + this.tweens.add = function ( config: | Phaser.Types.Tweens.TweenBuilderConfig @@ -33,71 +67,33 @@ export function initGameSpeed() { | Phaser.Tweens.Tween | Phaser.Tweens.TweenChain, ) { - if (config.loopDelay) { - config.loopDelay = transformValue(config.loopDelay as number); - } - - if (!(config instanceof Phaser.Tweens.TweenChain)) { - if (config.duration) { - config.duration = transformValue(config.duration); - } - - if (!(config instanceof Phaser.Tweens.Tween)) { - if (config.delay) { - config.delay = transformValue(config.delay as number); - } - if (config.repeatDelay) { - config.repeatDelay = transformValue(config.repeatDelay); - } - if (config.hold) { - config.hold = transformValue(config.hold); - } - } - } + mutateProperties(config); return originalTweensAdd.apply(this, [config]); - }; - const originalTweensChain = this.tweens.chain; + } as typeof originalTweensAdd; + + const originalTweensChain: TweenManager["chain"] = this.tweens.chain; this.tweens.chain = function (config: Phaser.Types.Tweens.TweenChainBuilderConfig): Phaser.Tweens.TweenChain { - if (config.tweens) { - for (const t of config.tweens) { - if (t.duration) { - t.duration = transformValue(t.duration); - } - if (t.delay) { - t.delay = transformValue(t.delay as number); - } - if (t.repeatDelay) { - t.repeatDelay = transformValue(t.repeatDelay); - } - if (t.loopDelay) { - t.loopDelay = transformValue(t.loopDelay as number); - } - if (t.hold) { - t.hold = transformValue(t.hold); - } - } - } + mutateProperties(config); return originalTweensChain.apply(this, [config]); - }; - const originalAddCounter = this.tweens.addCounter; + } as typeof originalTweensChain; + const originalAddCounter: TweenManager["addCounter"] = this.tweens.addCounter; + this.tweens.addCounter = function (config: Phaser.Types.Tweens.NumberTweenBuilderConfig) { - if (config.duration) { - config.duration = transformValue(config.duration); - } - if (config.delay) { - config.delay = transformValue(config.delay); - } - if (config.repeatDelay) { - config.repeatDelay = transformValue(config.repeatDelay); - } - if (config.loopDelay) { - config.loopDelay = transformValue(config.loopDelay as number); - } - if (config.hold) { - config.hold = transformValue(config.hold); - } + mutateProperties(config); return originalAddCounter.apply(this, [config]); - }; + } as typeof originalAddCounter; + + const originalCreate: TweenManager["create"] = this.tweens.create; + this.tweens.create = function (config: Phaser.Types.Tweens.TweenBuilderConfig) { + mutateProperties(config, true); + return originalCreate.apply(this, [config]); + } as typeof originalCreate; + + const originalAddMultiple: TweenManager["addMultiple"] = this.tweens.addMultiple; + this.tweens.addMultiple = function (config: Phaser.Types.Tweens.TweenBuilderConfig[]) { + mutateProperties(config, true); + return originalAddMultiple.apply(this, [config]); + } as typeof originalAddMultiple; const originalFadeOut = SoundFade.fadeOut; SoundFade.fadeOut = ((_scene: Phaser.Scene, sound: Phaser.Sound.BaseSound, duration: number, destroy?: boolean) => diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index 32e105aec66..789fa96958c 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -1,3 +1,5 @@ +/** biome-ignore-all lint/performance/noNamespaceImport: Convenience */ + import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; @@ -48,23 +50,18 @@ export const settingsMigrators: Readonly = [settingsMigr // import * as vA_B_C from "./versions/vA_B_C"; // --- v1.0.4 (and below) PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience (TODO: make this a file-wide ignore when Biome supports those) import * as v1_0_4 from "./versions/v1_0_4"; // --- v1.7.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_7_0 from "./versions/v1_7_0"; // --- v1.8.3 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_8_3 from "./versions/v1_8_3"; // --- v1.9.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_9_0 from "./versions/v1_9_0"; // --- v1.10.0 PATCHES --- // -// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_10_0 from "./versions/v1_10_0"; /** Current game version */ diff --git a/src/touch-controls.ts b/src/touch-controls.ts index e2b5ad05baa..5319976648f 100644 --- a/src/touch-controls.ts +++ b/src/touch-controls.ts @@ -6,8 +6,8 @@ const repeatInputDelayMillis = 250; export default class TouchControl { events: EventEmitter; - private buttonLock: string[] = new Array(); - private inputInterval: NodeJS.Timeout[] = new Array(); + private buttonLock: string[] = []; + private inputInterval: NodeJS.Timeout[] = []; /** Whether touch controls are disabled */ private disabled = false; /** Whether the last touch event has finished before disabling */ @@ -42,7 +42,7 @@ export default class TouchControl { document.querySelectorAll(".apad-button").forEach(element => this.preventElementZoom(element as HTMLElement)); // Select all elements with the 'data-key' attribute and bind keys to them for (const button of document.querySelectorAll("[data-key]")) { - // @ts-ignore - Bind the key to the button using the dataset key + // @ts-expect-error - Bind the key to the button using the dataset key this.bindKey(button, button.dataset.key); } } @@ -208,7 +208,7 @@ export function isMobile(): boolean { /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test( a, ) || - /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( a.substr(0, 4), ) ) { diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index e4f11e1c93c..f7a97ec673c 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -176,12 +176,12 @@ export class UiInputs { return; } switch (globalScene.ui?.getMode()) { + // biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay case UiMode.MESSAGE: { const messageHandler = globalScene.ui.getHandler(); if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) { return; } - // biome-ignore lint/suspicious/noFallthroughSwitchClause: falls through to show menu overlay } case UiMode.TITLE: case UiMode.COMMAND: diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index 9a2180eccee..6f8f6a76b34 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -52,7 +52,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { /** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */ private flyoutText: Phaser.GameObjects.Text[] = new Array(4); /** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */ - private moveInfo: MoveInfo[] = new Array(); + private moveInfo: MoveInfo[] = []; /** Current state of the flyout's visibility */ public flyoutVisible = false; diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index e19882b09cc..c069c6fffd7 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -41,24 +41,15 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.setup(); } - /** - * Sets the updating state and updates button states accordingly. - * If value is true (updating), disables the buttons; if false, enables the buttons. - * @param {boolean} value - The new updating state. - */ - set isUpdating(value) { + /** When set to `true`, disables the buttons; when set to `false`, enables the buttons. */ + get isUpdating(): boolean { + return this._isUpdating; + } + set isUpdating(value: boolean) { this._isUpdating = value; this.setButtonsState(!value); } - /** - * Gets the current updating state. - * @returns {boolean} - The current updating state. - */ - get isUpdating() { - return this._isUpdating; - } - setup() { const titleWindow = addWindow(0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN); this.add(titleWindow); diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 0c8d90fa138..7ff3a1b65ee 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -625,7 +625,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const infoContainer = this.gachaInfoContainers[gachaType]; switch (gachaType as GachaType) { case GachaType.LEGENDARY: { - const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(new Date().getTime())); + const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(Date.now())); const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite; pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false)); break; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 83ce88714f5..7d4ec845209 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -591,9 +591,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { // Auto-color options green/blue for good/bad by looking for (+)/(-) if (text) { - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; + const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0]; text = text.replace( - /(\(\+\)[^\(\[]*)/gi, + /(\(\+\)[^([]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + @@ -601,7 +601,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { primaryStyleString, ); text = text.replace( - /(\(\-\)[^\(\[]*)/gi, + /(\(-\)[^([]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 32a88ab36b2..9ec74e70b23 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -2057,7 +2057,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } let newSpecies: PokemonSpecies; if (this.filteredIndices) { - const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); + const index = this.filteredIndices.indexOf(this.species.speciesId); const newIndex = index <= 0 ? this.filteredIndices.length - 1 : index - 1; newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); } else { @@ -2096,7 +2096,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } let newSpecies: PokemonSpecies; if (this.filteredIndices) { - const index = this.filteredIndices.findIndex(id => id === this.species.speciesId); + const index = this.filteredIndices.indexOf(this.species.speciesId); const newIndex = index >= this.filteredIndices.length - 1 ? 0 : index + 1; newSpecies = getPokemonSpecies(this.filteredIndices[newIndex]); } else { @@ -2321,7 +2321,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -2786,7 +2786,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(true); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? } } diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 1732bb005d3..9169ca77999 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -1389,7 +1389,7 @@ export default class PokedexUiHandler extends MessageUiHandler { const fitsMoves = fitsMove1 && fitsMove2; if (fitsEggMove1 && !fitsLevelMove1) { - const em1 = eggMoves.findIndex(name => name === selectedMove1); + const em1 = eggMoves.indexOf(selectedMove1); if ((starterData.eggMoves & (1 << em1)) === 0) { data.eggMove1 = false; } else { @@ -1399,7 +1399,7 @@ export default class PokedexUiHandler extends MessageUiHandler { data.tmMove1 = true; } if (fitsEggMove2 && !fitsLevelMove2) { - const em2 = eggMoves.findIndex(name => name === selectedMove2); + const em2 = eggMoves.indexOf(selectedMove2); if ((starterData.eggMoves & (1 << em2)) === 0) { data.eggMove2 = false; } else { diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 78ab4a40407..76e343d018a 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -19,7 +19,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { TypeColor, TypeShadow } from "#app/enums/color"; import { getNatureStatMultiplier, getNatureName } from "../data/nature"; import { getVariantTint } from "#app/sprites/variant"; -// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` +// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts` import * as Modifier from "#app/modifier/modifier"; import type { SpeciesId } from "#enums/species-id"; import { PlayerGender } from "#enums/player-gender"; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 7b4d46203c9..7dee041fa63 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -2,7 +2,7 @@ import i18next from "i18next"; import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import { GameMode } from "../game-mode"; -// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` +// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts` import * as Modifier from "#app/modifier/modifier"; import type { SessionSaveData } from "../system/game-data"; import type PokemonData from "../system/pokemon-data"; diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index e3631c062df..769de7993ce 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -209,7 +209,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler settingFiltered.forEach((setting, s) => { // Convert the setting key from format 'Key_Name' to 'Key name' for display. - const settingName = setting.replace(/\_/g, " "); + const settingName = setting.replace(/_/g, " "); // Create and add a text object for the setting name to the scene. const isLock = this.settingBlacklisted.includes(this.setting[setting]); diff --git a/src/ui/settings/navigationMenu.ts b/src/ui/settings/navigationMenu.ts index ad3d4ccf0b5..be3726bd47d 100644 --- a/src/ui/settings/navigationMenu.ts +++ b/src/ui/settings/navigationMenu.ts @@ -16,7 +16,7 @@ export class NavigationManager { private static instance: NavigationManager; public modes: UiMode[]; public selectedMode: UiMode = UiMode.SETTINGS; - public navigationMenus: NavigationMenu[] = new Array(); + public navigationMenus: NavigationMenu[] = []; public labels: string[]; /** @@ -105,7 +105,7 @@ export class NavigationManager { export default class NavigationMenu extends Phaser.GameObjects.Container { private navigationIcons: InputsIcons; - protected headerTitles: Phaser.GameObjects.Text[] = new Array(); + protected headerTitles: Phaser.GameObjects.Text[] = []; /** * Creates an instance of NavigationMenu. diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 20f613fb694..3cb6222cafa 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2822,7 +2822,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { iconElement: GameObjects.Sprite, controlLabel: GameObjects.Text, ): void { - // biome-ignore lint/suspicious/noImplicitAnyLet: TODO let iconPath: string; // touch controls cannot be rebound as is, and are just emulating a keyboard event. // Additionally, since keyboard controls can be rebound (and will be displayed when they are), we need to have special handling for the touch controls @@ -2856,7 +2855,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting); } - // @ts-ignore: TODO can iconPath actually be undefined? + // @ts-expect-error: TODO can iconPath actually be undefined? iconElement.setTexture(gamepadType, iconPath); iconElement.setPosition(this.instructionRowX, this.instructionRowY); controlLabel.setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY); @@ -3481,7 +3480,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -4489,7 +4488,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); - //@ts-ignore + //@ts-expect-error this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id)); const props = globalScene.gameData.getSpeciesDexAttrProps( diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index d30322de293..f108faf1646 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -117,7 +117,7 @@ export default class SummaryUiHandler extends UiHandler { private pokemon: PlayerPokemon | null; private playerParty: boolean; - /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items**/ + /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items*/ private newMove: Move | null; private moveSelectFunction: Function | null; private transitioning: boolean; diff --git a/src/ui/text.ts b/src/ui/text.ts index 8812d8ee4a8..76c85bac5cf 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -300,7 +300,7 @@ export function getTextWithColors( ): string { // Apply primary styling before anything else let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; - const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))!][0]; + const primaryStyleString = [...text.match(new RegExp(/\[color=[^[]*\]\[shadow=[^[]*\]/i))!][0]; /* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY} * or it will look wrong in legacy mode because of the different window background color @@ -320,7 +320,7 @@ export function getTextWithColors( }); // Remove extra style block at the end - return text.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, ""); + return text.replace(/\[color=[^[]*\]\[shadow=[^[]*\]\[\/color\]\[\/shadow\]/gi, ""); } // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: This is a giant switch which is the best option. diff --git a/src/utils/common.ts b/src/utils/common.ts index e19e5976507..753d6ebb865 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -10,7 +10,7 @@ export const MissingTextureKey = "__MISSING"; export function toReadableString(str: string): string { return str - .replace(/\_/g, " ") + .replace(/_/g, " ") .split(" ") .map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`) .join(" "); @@ -201,19 +201,19 @@ export function formatLargeNumber(count: number, threshold: number): string { let suffix = ""; switch (Math.ceil(ret.length / 3) - 1) { case 1: - suffix = "K"; + suffix = i18next.t("common:abrThousand"); break; case 2: - suffix = "M"; + suffix = i18next.t("common:abrMillion"); break; case 3: - suffix = "B"; + suffix = i18next.t("common:abrBillion"); break; case 4: - suffix = "T"; + suffix = i18next.t("common:abrTrillion"); break; case 5: - suffix = "q"; + suffix = i18next.t("common:abrQuadrillion"); break; default: return "?"; @@ -227,15 +227,31 @@ export function formatLargeNumber(count: number, threshold: number): string { } // Abbreviations from 10^0 to 10^33 -const AbbreviationsLargeNumber: string[] = ["", "K", "M", "B", "t", "q", "Q", "s", "S", "o", "n", "d"]; +function getAbbreviationsLargeNumber(): string[] { + return [ + "", + i18next.t("common:abrThousand"), + i18next.t("common:abrMillion"), + i18next.t("common:abrBillion"), + i18next.t("common:abrTrillion"), + i18next.t("common:abrQuadrillion"), + i18next.t("common:abrQuintillion"), + i18next.t("common:abrSextillion"), + i18next.t("common:abrSeptillion"), + i18next.t("common:abrOctillion"), + i18next.t("common:abrNonillion"), + i18next.t("common:abrDecillion"), + ]; +} export function formatFancyLargeNumber(number: number, rounded = 3): string { + const abbreviations = getAbbreviationsLargeNumber(); let exponent: number; if (number < 1000) { exponent = 0; } else { - const maxExp = AbbreviationsLargeNumber.length - 1; + const maxExp = abbreviations.length - 1; exponent = Math.floor(Math.log(number) / Math.log(1000)); exponent = Math.min(exponent, maxExp); @@ -243,7 +259,7 @@ export function formatFancyLargeNumber(number: number, rounded = 3): string { number /= Math.pow(1000, exponent); } - return `${(exponent === 0) || number % 1 === 0 ? number : number.toFixed(rounded)}${AbbreviationsLargeNumber[exponent]}`; + return `${exponent === 0 || number % 1 === 0 ? number : number.toFixed(rounded)}${abbreviations[exponent]}`; } export function formatMoney(format: MoneyFormat, amount: number) { @@ -583,7 +599,7 @@ export function isBetween(num: number, min: number, max: number): boolean { * @param move the move for which the animation filename is needed */ export function animationFileName(move: MoveId): string { - return MoveId[move].toLowerCase().replace(/\_/g, "-"); + return MoveId[move].toLowerCase().replace(/_/g, "-"); } /** diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts index 5ed793c0451..b38e941b59a 100644 --- a/src/utils/cookies.ts +++ b/src/utils/cookies.ts @@ -2,7 +2,7 @@ import { isBeta } from "./utility-vars"; export function setCookie(cName: string, cValue: string): void { const expiration = new Date(); - expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3 /*7*/); + expiration.setTime(Date.now() + 3600000 * 24 * 30 * 3 /*7*/); document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; } diff --git a/test/abilities/cud_chew.test.ts b/test/abilities/cud_chew.test.ts index 70c282bf8a8..e563e7537dd 100644 --- a/test/abilities/cud_chew.test.ts +++ b/test/abilities/cud_chew.test.ts @@ -1,4 +1,4 @@ -import { RepeatBerryNextTurnAbAttr } from "#app/data/abilities/ability"; +import { CudChewConsumeBerryAbAttr } from "#app/data/abilities/ability"; import Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; @@ -196,7 +196,7 @@ describe("Abilities - Cud Chew", () => { describe("regurgiates berries", () => { it("re-triggers effects on eater without pushing to array", async () => { - const apply = vi.spyOn(RepeatBerryNextTurnAbAttr.prototype, "apply"); + const apply = vi.spyOn(CudChewConsumeBerryAbAttr.prototype, "apply"); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); const farigiraf = game.scene.getPlayerPokemon()!; diff --git a/test/abilities/gorilla_tactics.test.ts b/test/abilities/gorilla_tactics.test.ts index a8b09461ea0..330b4f51bcd 100644 --- a/test/abilities/gorilla_tactics.test.ts +++ b/test/abilities/gorilla_tactics.test.ts @@ -91,7 +91,7 @@ describe("Abilities - Gorilla Tactics", () => { game.move.select(MoveId.METRONOME); await game.phaseInterceptor.to("TurnEndPhase"); - // Gorilla Tactics should bypass dancer and instruct + // Gorilla Tactics should lock into Metronome, not tackle expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(true); expect(darmanitan.isMoveRestricted(MoveId.METRONOME)).toBe(false); expect(darmanitan.getLastXMoves(-1)).toEqual([ diff --git a/test/abilities/harvest.test.ts b/test/abilities/harvest.test.ts index 662eeed6dd0..42c9772bd10 100644 --- a/test/abilities/harvest.test.ts +++ b/test/abilities/harvest.test.ts @@ -95,7 +95,7 @@ describe("Abilities - Harvest", () => { // Give ourselves harvest and disable enemy neut gas, // but force our roll to fail so we don't accidentally recover anything - vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApplyPostTurn").mockReturnValueOnce(false); + vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApply").mockReturnValueOnce(false); game.override.ability(AbilityId.HARVEST); game.move.select(MoveId.GASTRO_ACID); await game.move.selectEnemyMove(MoveId.NUZZLE); diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts index b37c9effeb0..b21b04531ec 100644 --- a/test/abilities/healer.test.ts +++ b/test/abilities/healer.test.ts @@ -42,7 +42,7 @@ describe("Abilities - Healer", () => { }); it("should not queue a message phase for healing if the ally has fainted", async () => { - const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApplyPostTurn"); + const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApply"); game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); diff --git a/test/abilities/moody.test.ts b/test/abilities/moody.test.ts index a3e321928b8..bca3d57a70a 100644 --- a/test/abilities/moody.test.ts +++ b/test/abilities/moody.test.ts @@ -68,7 +68,7 @@ describe("Abilities - Moody", () => { }); it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => { - await game.classicMode.startBattle(); + await game.classicMode.startBattle([SpeciesId.MAGIKARP]); const playerPokemon = game.scene.getPlayerPokemon()!; diff --git a/test/abilities/mycelium_might.test.ts b/test/abilities/mycelium_might.test.ts index 2cdda4353b5..56af51c00f7 100644 --- a/test/abilities/mycelium_might.test.ts +++ b/test/abilities/mycelium_might.test.ts @@ -40,7 +40,7 @@ describe("Abilities - Mycelium Might", () => { * https://bulbapedia.bulbagarden.net/wiki/Mycelium_Might_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 - **/ + */ it("will move last in its priority bracket and ignore protective abilities", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); diff --git a/test/abilities/neutralizing_gas.test.ts b/test/abilities/neutralizing_gas.test.ts index 2408a78f11d..f153e71587e 100644 --- a/test/abilities/neutralizing_gas.test.ts +++ b/test/abilities/neutralizing_gas.test.ts @@ -178,7 +178,7 @@ describe("Abilities - Neutralizing Gas", () => { const enemy = game.scene.getEnemyPokemon()!; const weatherChangeAttr = enemy.getAbilityAttrs("PostSummonWeatherChangeAbAttr", false)[0]; - vi.spyOn(weatherChangeAttr, "applyPostSummon"); + const weatherChangeSpy = vi.spyOn(weatherChangeAttr, "apply"); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); @@ -187,6 +187,6 @@ describe("Abilities - Neutralizing Gas", () => { await game.killPokemon(game.scene.getPlayerPokemon()!); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); - expect(weatherChangeAttr.applyPostSummon).not.toHaveBeenCalled(); + expect(weatherChangeSpy).not.toHaveBeenCalled(); }); }); diff --git a/test/abilities/sand_veil.test.ts b/test/abilities/sand_veil.test.ts index 35a0a3347ff..035b37d85a8 100644 --- a/test/abilities/sand_veil.test.ts +++ b/test/abilities/sand_veil.test.ts @@ -1,3 +1,4 @@ +import type { StatMultiplierAbAttrParams } from "#app/@types/ability-types"; import { allAbilities } from "#app/data/data-lists"; import { CommandPhase } from "#app/phases/command-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; @@ -46,15 +47,13 @@ describe("Abilities - Sand Veil", () => { vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[AbilityId.SAND_VEIL]); const sandVeilAttr = allAbilities[AbilityId.SAND_VEIL].getAttrs("StatMultiplierAbAttr")[0]; - vi.spyOn(sandVeilAttr, "applyStatStage").mockImplementation( - (_pokemon, _passive, _simulated, stat, statValue, _args) => { - if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { - statValue.value *= -1; // will make all attacks miss - return true; - } - return false; - }, - ); + vi.spyOn(sandVeilAttr, "apply").mockImplementation(({ stat, statVal }: StatMultiplierAbAttrParams) => { + if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { + statVal.value *= -1; // will make all attacks miss + return true; + } + return false; + }); expect(leadPokemon[0].hasAbility(AbilityId.SAND_VEIL)).toBe(true); expect(leadPokemon[1].hasAbility(AbilityId.SAND_VEIL)).toBe(false); diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 6bb63fd16a5..a7896b9eeb8 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#enums/battler-index"; -import { applyAbAttrs, applyPreDefendAbAttrs } from "#app/data/abilities/apply-ab-attrs"; +import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { NumberHolder } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; @@ -52,25 +52,16 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(MoveId.AIR_SLASH); const chance = new NumberHolder(move.chance); - await applyAbAttrs( - "MoveEffectChanceMultiplierAbAttr", - phase.getUserPokemon()!, - null, - false, + applyAbAttrs("MoveEffectChanceMultiplierAbAttr", { + pokemon: phase.getUserPokemon()!, chance, move, - phase.getFirstTarget(), - false, - ); - await applyPreDefendAbAttrs( - "IgnoreMoveEffectsAbAttr", - phase.getFirstTarget()!, - phase.getUserPokemon()!, - null, - null, - false, + }); + applyAbAttrs("IgnoreMoveEffectsAbAttr", { + pokemon: phase.getFirstTarget()!, + move, chance, - ); + }); expect(chance.value).toBe(0); }); diff --git a/test/abilities/stall.test.ts b/test/abilities/stall.test.ts index e8ee23fb972..04a29a4f284 100644 --- a/test/abilities/stall.test.ts +++ b/test/abilities/stall.test.ts @@ -35,7 +35,7 @@ describe("Abilities - Stall", () => { * References: * https://bulbapedia.bulbagarden.net/wiki/Stall_(Ability) * https://bulbapedia.bulbagarden.net/wiki/Priority - **/ + */ it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 6e24e10d168..fff37daff7b 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -22,10 +22,7 @@ describe("Abilities - Unburden", () => { */ function getHeldItemCount(pokemon: Pokemon): number { const stackCounts = pokemon.getHeldItems().map(m => m.getStackCount()); - if (stackCounts.length) { - return stackCounts.reduce((a, b) => a + b); - } - return 0; + return stackCounts.reduce((a, b) => a + b, 0); } beforeAll(() => { @@ -277,7 +274,7 @@ describe("Abilities - Unburden", () => { const initialTreeckoSpeed = treecko.getStat(Stat.SPD); const initialPurrloinSpeed = purrloin.getStat(Stat.SPD); const unburdenAttr = treecko.getAbilityAttrs("PostItemLostAbAttr")[0]; - vi.spyOn(unburdenAttr, "applyPostItemLost"); + vi.spyOn(unburdenAttr, "apply"); // Player uses Baton Pass, which also passes the Baton item game.move.select(MoveId.BATON_PASS); @@ -288,7 +285,7 @@ describe("Abilities - Unburden", () => { expect(getHeldItemCount(purrloin)).toBe(1); expect(treecko.getEffectiveStat(Stat.SPD)).toBe(initialTreeckoSpeed); expect(purrloin.getEffectiveStat(Stat.SPD)).toBe(initialPurrloinSpeed); - expect(unburdenAttr.applyPostItemLost).not.toHaveBeenCalled(); + expect(unburdenAttr.apply).not.toHaveBeenCalled(); }); it("should not speed up a Pokemon after it loses the ability Unburden", async () => { diff --git a/test/abilities/wonder_skin.test.ts b/test/abilities/wonder_skin.test.ts index 886882ab6fd..177b97ae3b7 100644 --- a/test/abilities/wonder_skin.test.ts +++ b/test/abilities/wonder_skin.test.ts @@ -66,7 +66,7 @@ describe("Abilities - Wonder Skin", () => { it(`does not affect pokemon with ${ability[1]}`, async () => { const moveToCheck = allMoves[MoveId.CHARM]; - // @ts-ignore ts doesn't know that ability[0] is an ability and not a string... + // @ts-expect-error ts doesn't know that ability[0] is an ability and not a string... game.override.ability(ability[0]); vi.spyOn(moveToCheck, "calculateBattleAccuracy"); diff --git a/test/field/pokemon-id-checks.test.ts b/test/field/pokemon-id-checks.test.ts new file mode 100644 index 00000000000..4023b8d73ad --- /dev/null +++ b/test/field/pokemon-id-checks.test.ts @@ -0,0 +1,79 @@ +import type Pokemon from "#app/field/pokemon"; +import { MoveId } from "#enums/move-id"; +import { AbilityId } from "#enums/ability-id"; +import { SpeciesId } from "#enums/species-id"; +import { BattleType } from "#enums/battle-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { BattlerIndex } from "#enums/battler-index"; + +describe("Field - Pokemon ID Checks", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(AbilityId.NO_GUARD) + .battleStyle("single") + .battleType(BattleType.TRAINER) + .criticalHits(false) + .enemyLevel(100) + .enemySpecies(SpeciesId.ARCANINE) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH); + }); + + function onlyUnique(array: T[]): T[] { + return [...new Set(array)]; + } + + // TODO: We currently generate IDs as a pure random integer; enable once unique UUIDs are added + it.todo("2 Pokemon should not be able to generate with the same ID during 1 encounter", async () => { + game.override.battleType(BattleType.TRAINER); // enemy generates 2 mons + await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.ABRA]); + + const ids = (game.scene.getPlayerParty() as Pokemon[]).concat(game.scene.getEnemyParty()).map((p: Pokemon) => p.id); + const uniqueIds = onlyUnique(ids); + + expect(ids).toHaveLength(uniqueIds.length); + }); + + it("should not prevent Battler Tags from triggering if user has PID of 0", async () => { + await game.classicMode.startBattle([SpeciesId.TREECKO, SpeciesId.AERODACTYL]); + + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + // Override player pokemon PID to be 0 + player.id = 0; + expect(player.getTag(BattlerTagType.DESTINY_BOND)).toBeUndefined(); + + game.move.use(MoveId.DESTINY_BOND); + game.doSelectPartyPokemon(1); + await game.move.forceEnemyMove(MoveId.FLAME_WHEEL); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + const dBondTag = player.getTag(BattlerTagType.DESTINY_BOND)!; + expect(dBondTag).toBeDefined(); + expect(dBondTag.sourceId).toBe(0); + expect(dBondTag.getSourcePokemon()).toBe(player); + + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(player.isFainted()).toBe(true); + expect(enemy.isFainted()).toBe(true); + }); +}); diff --git a/test/field/pokemon.test.ts b/test/field/pokemon.test.ts index 774d46b18fe..c6524e7397f 100644 --- a/test/field/pokemon.test.ts +++ b/test/field/pokemon.test.ts @@ -31,7 +31,7 @@ describe("Spec - Pokemon", () => { const pkm = game.scene.getPlayerPokemon()!; expect(pkm).toBeDefined(); - expect(pkm.trySetStatus(undefined)).toBe(true); + expect(pkm.trySetStatus(undefined)).toBe(false); }); describe("Add To Party", () => { diff --git a/test/moves/safeguard.test.ts b/test/moves/safeguard.test.ts index 8d5303e3feb..91aa298a8ca 100644 --- a/test/moves/safeguard.test.ts +++ b/test/moves/safeguard.test.ts @@ -140,9 +140,8 @@ describe("Moves - Safeguard", () => { game.field.mockAbility(player, AbilityId.STATIC); vi.spyOn( allAbilities[AbilityId.STATIC].getAttrs("PostDefendContactApplyStatusEffectAbAttr")[0], - "chance", - "get", - ).mockReturnValue(100); + "canApply", + ).mockReturnValue(true); game.move.select(MoveId.SPLASH); await game.move.forceEnemyMove(MoveId.SAFEGUARD); diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts index 6954d6212cc..6293a49cf63 100644 --- a/test/mystery-encounter/encounter-test-utils.ts +++ b/test/mystery-encounter/encounter-test-utils.ts @@ -1,4 +1,4 @@ -// biome-ignore lint/style/noNamespaceImport: Necessary for mocks +// biome-ignore lint/performance/noNamespaceImport: Necessary for mocks import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { Status } from "#app/data/status-effect"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/sprites/pokemonSprite.test.ts b/test/sprites/pokemonSprite.test.ts index a008b75b42e..be188503b1e 100644 --- a/test/sprites/pokemonSprite.test.ts +++ b/test/sprites/pokemonSprite.test.ts @@ -26,9 +26,9 @@ describe("check if every variant's sprite are correctly set", () => { femaleVariant = masterlist.female; backVariant = masterlist.back; - // @ts-ignore + // @ts-expect-error delete masterlist.female; // TODO: resolve ts-ignore - //@ts-ignore + //@ts-expect-error delete masterlist.back; //TODO: resolve ts-ignore }); diff --git a/test/testUtils/gameWrapper.ts b/test/testUtils/gameWrapper.ts index 9264b68d421..7b5d564de2e 100644 --- a/test/testUtils/gameWrapper.ts +++ b/test/testUtils/gameWrapper.ts @@ -15,15 +15,15 @@ import { vi } from "vitest"; import { version } from "../../package.json"; import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; import { MockTimedEventManager } from "./mocks/mockTimedEventManager"; +import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; +import MockContainer from "./mocks/mocksContainer/mockContainer"; import InputManager = Phaser.Input.InputManager; import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; -import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; -import MockContainer from "./mocks/mocksContainer/mockContainer"; -// biome-ignore lint/style/noNamespaceImport: Necessary in order to mock the var +// biome-ignore lint/performance/noNamespaceImport: Necessary in order to mock the var import * as bypassLoginModule from "#app/global-vars/bypass-login"; window.URL.createObjectURL = (blob: Blob) => { @@ -122,15 +122,20 @@ export default class GameWrapper { }, }; + // TODO: Replace this with a proper mock of phaser's TweenManager. this.scene.tweens = { add: data => { - if (data.onComplete) { - data.onComplete(); - } + // TODO: our mock of `add` should have the same signature as the real one, which returns the tween + data.onComplete?.(); }, getTweensOf: () => [], killTweensOf: () => [], - chain: () => null, + + chain: data => { + // TODO: our mock of `chain` should have the same signature as the real one, which returns the chain + data?.tweens?.forEach(tween => tween.onComplete?.()); + data.onComplete?.(); + }, addCounter: data => { if (data.onComplete) { data.onComplete(); diff --git a/test/testUtils/inputsHandler.ts b/test/testUtils/inputsHandler.ts index 6e9dd453541..0d8c43e7616 100644 --- a/test/testUtils/inputsHandler.ts +++ b/test/testUtils/inputsHandler.ts @@ -89,7 +89,7 @@ class Fakepad extends Phaser.Input.Gamepad.Gamepad { public index: number; constructor(pad) { - //@ts-ignore + //@ts-expect-error super(undefined, { ...pad, buttons: pad.deviceMapping, axes: [] }); //TODO: resolve ts-ignore this.id = "xbox_360_fakepad"; this.index = 0; diff --git a/test/testUtils/mocks/mockVideoGameObject.ts b/test/testUtils/mocks/mockVideoGameObject.ts index 1789229b1c7..9b25877c80c 100644 --- a/test/testUtils/mocks/mockVideoGameObject.ts +++ b/test/testUtils/mocks/mockVideoGameObject.ts @@ -5,7 +5,7 @@ export class MockVideoGameObject implements MockGameObject { public name: string; public active = true; - public play = () => null; + public play = () => this; public stop = () => this; public setOrigin = () => this; public setScale = () => this; diff --git a/test/testUtils/mocks/mocksContainer/mockImage.ts b/test/testUtils/mocks/mocksContainer/mockImage.ts index d20b4663771..f8990985e54 100644 --- a/test/testUtils/mocks/mocksContainer/mockImage.ts +++ b/test/testUtils/mocks/mocksContainer/mockImage.ts @@ -1,6 +1,7 @@ import MockContainer from "#test/testUtils/mocks/mocksContainer/mockContainer"; export class MockImage extends MockContainer { + // biome-ignore lint/correctness/noUnusedPrivateClassMembers: this is intentional (?) private texture; constructor(textureManager, x, y, texture) { diff --git a/test/testUtils/mocks/mocksContainer/mockSprite.ts b/test/testUtils/mocks/mocksContainer/mockSprite.ts index ed1f1df6609..bbcd421e1a7 100644 --- a/test/testUtils/mocks/mocksContainer/mockSprite.ts +++ b/test/testUtils/mocks/mocksContainer/mockSprite.ts @@ -19,13 +19,13 @@ export default class MockSprite implements MockGameObject { constructor(textureManager, x, y, texture) { this.textureManager = textureManager; this.scene = textureManager.scene; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame; - // @ts-ignore + // @ts-expect-error Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame; // Phaser.GameObjects.Sprite.prototype.disable = this.disable; diff --git a/test/testUtils/testFileInitialization.ts b/test/testUtils/testFileInitialization.ts index da390870300..578747b0529 100644 --- a/test/testUtils/testFileInitialization.ts +++ b/test/testUtils/testFileInitialization.ts @@ -47,7 +47,7 @@ export function initTestFile() { }); BBCodeText.prototype.destroy = () => null; - // @ts-ignore + // @ts-expect-error BBCodeText.prototype.resize = () => null; InputText.prototype.setElement = () => null as any; InputText.prototype.resize = () => null as any;